Vaadin,JPA,Spring-具有组合主键的实体的简单形式

问题描述 投票:0回答:1

我有一个简单的数据库模型,可以嵌套产品类别,如下所示:database model

而且我想在Vaadin中创建基本表单,该表单具有要创建的两个组合框分解后的实体

无法绑定组合键是用@Embeddable和@EmbeddedId 到我的表单字段构造的。

当我单击按钮“添加类别”时,将弹出一个空白表格,我收到此错误

java.lang.NullPointerException: null
    at java.base/java.lang.reflect.Method.invoke(Method.java:559) ~[na:na]
    at com.vaadin.flow.data.binder.BeanPropertySet.invokeWrapExceptions(BeanPropertySet.java:516) ~[flow-data-2.1.8.jar:2.1.8]
    at com.vaadin.flow.data.binder.BeanPropertySet.access$600(BeanPropertySet.java:48) ~[flow-data-2.1.8.jar:2.1.8]
    at com.vaadin.flow.data.binder.BeanPropertySet$NestedBeanPropertyDefinition.lambda$getGetter$3ec26976$1(BeanPropertySet.java:200) ~[flow-data-2.1.8.jar:2.1.8]
    at com.vaadin.flow.data.binder.Binder$BindingImpl.initFieldValue(Binder.java:1130) ~[flow-data-2.1.8.jar:2.1.8]
    at com.vaadin.flow.data.binder.Binder$BindingImpl.access$200(Binder.java:972) ~[flow-data-2.1.8.jar:2.1.8]
    at com.vaadin.flow.data.binder.Binder.lambda$setBean$1(Binder.java:1677) ~[flow-data-2.1.8.jar:2.1.8]
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) ~[na:na]
    at com.vaadin.flow.data.binder.Binder.setBean(Binder.java:1677) ~[flow-data-2.1.8.jar:2.1.8]
    at cz.cvut.fit.manufacturingservices.ui.admin.view.product.category.ProductCategoryProductCategoryForm.setProductCategoryProductCategory(ProductCategoryProductCategoryForm.java:62) ~[main/:na]
    at cz.cvut.fit.manufacturingservices.ui.admin.view.product.category.ProductCategoryProductCategoryView.editProductCategoryProductCategory(ProductCategoryProductCategoryView.java:64) ~[main/:na]
    at cz.cvut.fit.manufacturingservices.ui.admin.view.product.category.ProductCategoryProductCategoryView.addProductCategoryProductCategory(ProductCategoryProductCategoryView.java:94) ~[main/:na]
    at cz.cvut.fit.manufacturingservices.ui.admin.view.product.category.ProductCategoryProductCategoryView.lambda$getToolbar$2f54d9f7$1(ProductCategoryProductCategoryView.java:78) ~[main/:na]
    at com.vaadin.flow.component.ComponentEventBus.fireEventForListener(ComponentEventBus.java:205) ~[flow-server-2.1.8.jar:2.1.8]
    at com.vaadin.flow.component.ComponentEventBus.handleDomEvent(ComponentEventBus.java:373) ~[flow-server-2.1.8.jar:2.1.8]
    at com.vaadin.flow.component.ComponentEventBus.lambda$addDomTrigger$dd1b7957$1(ComponentEventBus.java:264) ~[flow-server-2.1.8.jar:2.1.8]
    at com.vaadin.flow.internal.nodefeature.ElementListenerMap.lambda$fireEvent$2(ElementListenerMap.java:441) ~[flow-server-2.1.8.jar:2.1.8]
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) ~[na:na]
    at com.vaadin.flow.internal.nodefeature.ElementListenerMap.fireEvent(ElementListenerMap.java:441) ~[flow-server-2.1.8.jar:2.1.8]
    at com.vaadin.flow.server.communication.rpc.EventRpcHandler.handleNode(EventRpcHandler.java:59) ~[flow-server-2.1.8.jar:2.1.8]
    at com.vaadin.flow.server.communication.rpc.AbstractRpcInvocationHandler.handle(AbstractRpcInvocationHandler.java:64) ~[flow-server-2.1.8.jar:2.1.8]
    at com.vaadin.flow.server.communication.ServerRpcHandler.handleInvocationData(ServerRpcHandler.java:409) ~[flow-server-2.1.8.jar:2.1.8]
    at com.vaadin.flow.server.communication.ServerRpcHandler.lambda$handleInvocations$1(ServerRpcHandler.java:390) ~[flow-server-2.1.8.jar:2.1.8]
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) ~[na:na]
    at com.vaadin.flow.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:390) ~[flow-server-2.1.8.jar:2.1.8]
    at com.vaadin.flow.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:317) ~[flow-server-2.1.8.jar:2.1.8]
    at com.vaadin.flow.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:89) ~[flow-server-2.1.8.jar:2.1.8]
    at com.vaadin.flow.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40) ~[flow-server-2.1.8.jar:2.1.8]
    at com.vaadin.flow.server.VaadinService.handleRequest(VaadinService.java:1540) ~[flow-server-2.1.8.jar:2.1.8]
    at com.vaadin.flow.server.VaadinServlet.service(VaadinServlet.java:247) ~[flow-server-2.1.8.jar:2.1.8]
    at com.vaadin.flow.spring.SpringServlet.service(SpringServlet.java:120) ~[vaadin-spring-12.1.4.jar:na]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:712) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:459) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:352) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:312) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.springframework.web.servlet.mvc.ServletForwardingController.handleRequestInternal(ServletForwardingController.java:141) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:177) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:52) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1594) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.33.jar:9.0.33]
    at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]


我的组合键实现:

@Embeddable
public class ProductCategoryProductCategoryIdentity implements Serializable {

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "product_category_parent_id", nullable = false)
    private ProductCategory productCategoryParent;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "product_category_child_id", nullable = false)
    private ProductCategory productCategoryChild;

    public ProductCategory getProductCategoryParent() {
        return productCategoryParent;
    }

    public void setProductCategoryParent(ProductCategory productCategoryParent) {
        this.productCategoryParent = productCategoryParent;
    }

    public ProductCategory getProductCategoryChild() {
        return productCategoryChild;
    }

    public void setProductCategoryChild(ProductCategory productCategoryChild) {
        this.productCategoryChild = productCategoryChild;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof ProductCategoryProductCategoryIdentity)) return false;
        ProductCategoryProductCategoryIdentity that = (ProductCategoryProductCategoryIdentity) o;
        return Objects.equals(getProductCategoryParent(), that.getProductCategoryParent()) &&
                Objects.equals(getProductCategoryChild(), that.getProductCategoryChild());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getProductCategoryParent(), getProductCategoryChild());
    }
}

存储库实现:

public interface ProductCategoryProductCategoryRepository extends JpaRepository<ProductCategoryProductCategory, ProductCategoryProductCategoryIdentity> {
}

服务实现:

@Service
public class ProductCategoryProductCategoryService {

    private static final Logger LOGGER = Logger.getLogger(ProductCategoryProductCategory.class.getName());

    @Autowired
    private ProductCategoryProductCategoryRepository productCategoryProductCategoryRepository;

    public List<ProductCategoryProductCategory> findAll() {
        return productCategoryProductCategoryRepository.findAll();
    }

    public void save(ProductCategoryProductCategory productCategoryProductCategory) {
        if (productCategoryProductCategory == null) {
            LOGGER.log(Level.SEVERE, "Cannot save null product category decomposition");
            return;
        }
        productCategoryProductCategoryRepository.save(productCategoryProductCategory);
    }
}

表单组件实现:

public class ProductCategoryProductCategoryForm extends FormLayout {
    ComboBox<ProductCategory> productCategoryParent = new ComboBox<>("Parent product category");
    ComboBox<ProductCategory> productCategoryChild = new ComboBox<>("Child product category");

    Button save = new Button("Save");
    Button close = new Button("Close");

    Binder<ProductCategoryProductCategory> binder = new BeanValidationBinder<>(ProductCategoryProductCategory.class);

    public ProductCategoryProductCategoryForm(List<ProductCategory> productCategories) {
        addClassName("product_category-form");

        productCategoryParent.setItems(productCategories);
        productCategoryParent.setItemLabelGenerator(ProductCategory::getLabel);
        productCategoryChild.setItems(productCategories);
        productCategoryChild.setItemLabelGenerator(ProductCategory::getLabel);

        //binder.bind(productCategoryParent, "id.productCategoryParent");
        //binder.bind(productCategoryChild, "id.productCategoryChild");

        //binder.bindInstanceFields(this);
        add(productCategoryParent, productCategoryChild, createButtonsLayout());
    }


    private Component createButtonsLayout() {
        save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        close.addThemeVariants(ButtonVariant.LUMO_TERTIARY);

        save.addClickShortcut(Key.ENTER);
        close.addClickShortcut(Key.ESCAPE);

        save.addClickListener(event -> validateAndSave());
        close.addClickListener(event -> fireEvent(new CloseEvent(this)));

        binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid()));

        return new HorizontalLayout(save, close);
    }

    public void setProductCategoryProductCategory(ProductCategoryProductCategory productCategoryProductCategory) {
        binder.setBean(productCategoryProductCategory);
    }

    private void validateAndSave() {
        if (binder.isValid()) {
            fireEvent(new SaveEvent(this, binder.getBean()));
        }
    }


    public static abstract class ProductCategoryProductCategoryFormEvent extends ComponentEvent<ProductCategoryProductCategoryForm> {
        private final ProductCategoryProductCategory productCategoryProductCategory;

        protected ProductCategoryProductCategoryFormEvent(ProductCategoryProductCategoryForm source, ProductCategoryProductCategory productCategoryProductCategory) {
            super(source, false);
            this.productCategoryProductCategory = productCategoryProductCategory;
        }

        public ProductCategoryProductCategory getProductCategoryProductCategory() {
            return productCategoryProductCategory;
        }
    }

    public static class SaveEvent extends ProductCategoryProductCategoryForm.ProductCategoryProductCategoryFormEvent {
        SaveEvent(ProductCategoryProductCategoryForm source, ProductCategoryProductCategory productCategoryProductCategory) {
            super(source, productCategoryProductCategory);
        }
    }

    public static class DeleteEvent extends ProductCategoryProductCategoryForm.ProductCategoryProductCategoryFormEvent {
        DeleteEvent(ProductCategoryProductCategoryForm source, ProductCategoryProductCategory productCategoryProductCategory) {
            super(source, productCategoryProductCategory);
        }

    }

    public static class CloseEvent extends ProductCategoryProductCategoryForm.ProductCategoryProductCategoryFormEvent {
        CloseEvent(ProductCategoryProductCategoryForm source) {
            super(source, null);
        }
    }

    public <T extends ComponentEvent<?>> Registration addListener(Class<T> eventType, ComponentEventListener<T> listener) {
        return getEventBus().addListener(eventType, listener);
    }
}

查看实现:

@Route(value = "product-category-product-category", layout = MainLayout.class)
public class ProductCategoryProductCategoryView extends VerticalLayout {

    private final Grid<ProductCategoryProductCategory> grid = new Grid<>(ProductCategoryProductCategory.class);
    private final ProductCategoryProductCategoryForm form;

    @Autowired
    private ProductCategoryProductCategoryService productCategoryProductCategoryService;

    public ProductCategoryProductCategoryView(ProductCategoryService productCategoryService) {
        form = new ProductCategoryProductCategoryForm(productCategoryService.findAll());
    }

    @PostConstruct
    public void init() {
        addClassName("product_category-view");
        setSizeFull();

        configureGrid();
        configureForm();

        add(getToolbar(), getContent());

        updateList();
        closeEditor();
    }

    private void configureForm() {
        form.addListener(ProductCategoryProductCategoryForm.SaveEvent.class, this::saveProductCategoryProductCategory);
        form.addListener(ProductCategoryProductCategoryForm.CloseEvent.class, e -> closeEditor());
    }

    private void configureGrid() {
        grid.addClassName("product_category-grid");
        grid.setSizeFull();

        //grid.setColumns("parentId", "childId");

        grid.asSingleSelect().addValueChangeListener(evn -> editProductCategoryProductCategory(evn.getValue()));
    }

    private void editProductCategoryProductCategory(ProductCategoryProductCategory productCategoryProductCategory) {
        if (productCategoryProductCategory == null)
            closeEditor();
        else {
            form.setProductCategoryProductCategory(productCategoryProductCategory);
            form.setVisible(true);
            addClassName("editing");
        }
    }

    private void closeEditor() {
        form.setProductCategoryProductCategory(null);
        form.setVisible(false);
        removeClassName("editing");
    }

    private HorizontalLayout getToolbar() {
        Button addProductCategoryButton = new Button("Add category");
        addProductCategoryButton.addClickListener(click -> addProductCategoryProductCategory());

        HorizontalLayout toolbar = new HorizontalLayout(addProductCategoryButton);
        toolbar.addClassName("toolbar");
        return toolbar;
    }

    private Component getContent() {
        Div content = new Div(grid, form);
        content.setSizeFull();
        content.addClassName("content");
        return content;
    }

    private void addProductCategoryProductCategory() {
        grid.asSingleSelect().clear();
        editProductCategoryProductCategory(new ProductCategoryProductCategory());
    }

    private void updateList() {
        grid.setItems(productCategoryProductCategoryService.findAll());
    }

    private void saveProductCategoryProductCategory(ProductCategoryProductCategoryForm.SaveEvent event) {
        productCategoryProductCategoryService.save(event.getProductCategoryProductCategory());
        updateList();
        closeEditor();
    }
}

您能帮我解决这个问题,还是向我提供带有组合主(外)键的简单实体以及创建该实体的简单形式?

非常感谢。

spring spring-boot jpa spring-data-jpa vaadin
1个回答
0
投票

错误日志说,在ProductCategoryProductCategory期间尝试访问binder::setBean实例的绑定属性时,存在NullPointerException。我猜它的可嵌入ID就是null(无需阅读所有代码),但是您应该在绑定之前使用所有需要的值初始化对象,或者在绑定中添加nullRepresentation

实际上,因为您具有嵌套的属性绑定,所以我认为您还需要不通过property-string来声明绑定,因此您可以在分别调用item.getId()或它的setter之前为item.getId().getProductCategoryParent()添加空检查。 >

binder.forField()
    .withNullRepresentation("") // if the id's parent is null, show empty string
    .bind(                      // but if the id itself is null, don't try to get its parent
        (item) -> item.getId() == null ? null : item.getId().getProductCategoryParent(), 
        (item, value) -> {
            if(item.getId() != null){
                item.getId().setProductCategoryParent(value);
            }
        }
);
热门问题
推荐问题
最新问题