У меня простая модель базы данных с возможностью вкладывать категории продуктов, например:
И я хочу создать базовую форму c в Vaadin, которая имеет два ComboBox для создания этого разложенного объекта .
I не может связать составной ключ , созданный с помощью @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]
My Внедрение композитного ключа:
@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();
}
}
Не могли бы вы помочь мне решить эту проблему или предоставить мне простой объект с составленным первичным (внешним) ключом вместе с простой формой для создания этого объекта?
Большое спасибо.