问题
I'm using VAADIN 8 and struggling with a problem. I've bind all fields on FormLayout and want to save values from these fields in a db. All fields are filled with values. The essence of the problem is that the DateField value is sent as null when the object is saved to the database
The question is why DateFiled has null value and how to win the problem?
VAADIN v.8.6.2
Entity (simplified):
@Entity
@DynamicInsert
@DynamicUpdate
@Table(name = "Orders")
public class Order{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "OrderID", nullable = false)
private Long orderID;
@Column(name = "Description", nullable = false, length = 1000)
private String description;
@Column(name = "CreationDate", nullable = false)
private Date creationDate;
public Long getOrderID() { return orderID; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Date getCreationDate() { return creationDate; }
public void setCreationDate(Date creationDate) { this.creationDate = creationDate; }
}
OrderService(simplified):
public class OrderService {
private static OrderDao orderService;
public OrderService() {
orderService = new OrderDao();
}
public void persist(Order entity) {
orderService.openCurrentSessionwithTransaction();
orderService.persist(entity);
orderService.closeCurrentSessionwithTransaction();
}
}
Binding(simplified):
Binder<Order> binder = new Binder<>(Order.class);
FormLayout formLayout = new FormLayout();
TextField description = new TextField("Description");
DateField creationDate = new DateField("Creation Date");
private Button save = new Button("Save");
private Button cancel = new Button("Cancel");
HorizontalLayout actions = new HorizontalLayout();
actions.addComponents(save, cancel);
formLayout.addComponents(clientId, description, creationDate, actions);
creationDate.setDateFormat("yyyy-MM-dd HH:mm:ss");
binder.forField(this.clientId)
.bind(Order::getClientID, Order::setClientID);
binder.forField(this.description)
.bind(Order::getDescription, Order::setDescription);
binder.forField(this.creationDate)
.withConverter(new SqlDateToLocalDateConverter())
.bind(Order::getCreationDate, Order::setCreationDate); //the likely problem
binder.bindInstanceFields(this);
binder.setBean(order);
save.addClickListener(e -> save());
cancel.addClickListener(e -> close());
Converter class:
import com.vaadin.data.Converter;
import com.vaadin.data.Result;
import com.vaadin.data.ValueContext;
import java.sql.Date;
import java.time.LocalDate;
public class SqlDateToLocalDateConverter implements Converter<LocalDate, Date> {
@Override
public Result<java.sql.Date> convertToModel(LocalDate value, ValueContext context) {
if (value == null) {
return Result.ok(null);
}
return Result.ok( java.sql.Date.valueOf(value) );
}
@Override
public LocalDate convertToPresentation(java.sql.Date value, ValueContext context) {
if (value == null) {
return LocalDate.now();
}
return value.toLocalDate();
}
}
debug mode indicates that the creationDate field is null:
stacktrace:
org.hibernate.PropertyValueException: not-null property references a null or transient value : com.haulmont.testtask.model.Order.creationDate
at org.hibernate.engine.internal.Nullability.checkNullability(Nullability.java:108)
at org.hibernate.engine.internal.Nullability.checkNullability(Nullability.java:56)
at org.hibernate.action.internal.AbstractEntityInsertAction.nullifyTransientReferencesIfNotAlready(AbstractEntityInsertAction.java:115)
at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:69)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:645)
at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:282)
at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:263)
at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:317)
at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:359)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:292)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:200)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:131)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:192)
at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:38)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:177)
at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:32)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:709)
at org.hibernate.internal.SessionImpl.save(SessionImpl.java:701)
at org.hibernate.internal.SessionImpl.save(SessionImpl.java:696)
at com.haulmont.testtask.dao.OrderDao.persist(OrderDao.java:67)
at com.haulmont.testtask.service.OrderService.persist(OrderService.java:18)
at com.haulmont.testtask.ui.AddOrderModalView.save(AddOrderModalView.java:187)
at com.haulmont.testtask.ui.AddOrderModalView.lambda$new$61446b05$1(AddOrderModalView.java:111)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:499)
at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:273)
at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:237)
at com.vaadin.server.AbstractClientConnector.fireEvent(AbstractClientConnector.java:1014)
at com.vaadin.ui.Button.fireClick(Button.java:384)
at com.vaadin.ui.Button$1.click(Button.java:57)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:155)
at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:116)
at com.vaadin.server.communication.ServerRpcHandler.handleInvocation(ServerRpcHandler.java:445)
at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:410)
at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:274)
at com.vaadin.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:90)
at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40)
at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1601)
at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:445)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:845)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1689)
at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:225)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1676)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:581)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:226)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1174)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:511)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1106)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:213)
at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:119)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
at org.eclipse.jetty.server.Server.handle(Server.java:524)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:319)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:253)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:273)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)
at org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:93)
at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.executeProduceConsume(ExecuteProduceConsume.java:303)
at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:148)
at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:136)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)
at java.lang.Thread.run(Thread.java:748)
回答1:
Everything is okay except one little detail in your custom Converter class: there is one incocnsistency with the usage of Date class.
Three times you use java.sql.Date as Identifier of the class, but one time you forgot it (implements Converter<LocalDate, Date>
) and there it mistakingly took java.utils.Date.
Adding the package path java.sql.Date
there as well will do the trick
import com.vaadin.data.Converter;
import com.vaadin.data.Result;
import com.vaadin.data.ValueContext;
import java.sql.Date;
import java.time.LocalDate;
public class SqlDateToLocalDateConverter implements Converter<LocalDate, java.sql.Date> {
@Override
public Result<java.sql.Date> convertToModel(LocalDate value, ValueContext context) {
if (value == null) {
return Result.ok(null);
}
return Result.ok( java.sql.Date.valueOf(value) );
}
@Override
public LocalDate convertToPresentation(java.sql.Date value, ValueContext context) {
if (value == null) {
return LocalDate.now();
}
return value.toLocalDate();
}
}
来源:https://stackoverflow.com/questions/53738585/vaadin-datefield-saves-null-value-to-database