I am using Play Framework 2.3 with Java and I have the following one-to-many relationship between a committee and its recorded meeting dates:
@Entity
public class Committee {
@Id
protected Long id;
@OneToMany(cascade=CascadeType.ALL, mappedBy="committee", orphanRemoval=true)
private List<MeetingDate> meetingDates;
}
@Entity
public class MeetingDate {
@Id
private Long id;
@Constraints.Required
@JoinColumn(nullable=false)
@ManyToOne
private Committee committee;
@Constraints.Required
@Formats.DateTime(pattern="dd MMM yyyy")
@Temporal(TemporalType.DATE)
@Column(nullable=false)
private Date meetingDate;
}
I have a form that allows the user to edit a committee and the associated meeting dates on the one page. I've followed the "Forms" sample application from https://www.playframework.com/documentation/2.2.x/Samples and my template code looks like this:
@meetingDateGroup(field: Field, className: String = "meeting-date") = {
<tr class="repeating-group @className">
<td>
<input type="hidden" id='@field("id").name' name='@field("id").name' value='@field("id").value'>
<input type="text" id='@field("meetingDate").name' name='@field("meetingDate").name' value='@field("meetingDate").value' class="form-control">
@if(field("meetingDate").hasErrors){
<div class="help-block"><span class="glyphicon glyphicon-exclamation-sign"></span> @field("meetingDate").errors.mkString(", ")</div>
}
</td>
<td align="right"><a href="javascript:void(0)" class="remove-meeting-date rowIcon"><span class="glyphicon glyphicon-remove"></span> Delete</a></td>
</tr>
}
...
@helper.repeat(form("meetingDates"), min=1) { meetingDate =>
@meetingDateGroup(meetingDate)
}
@**
* Keep a hidden block that will be used as template for Javascript copy code
**@
@meetingDateGroup(
form("meetingDates[x]"), className = "hidden meeting-date-template"
)
Note that there is extra Javascript code (not shown) to add and remove the meeting date rows from the table dynamically. This Javascript code was copied from the Forms sample application.
The problem I have is that validation is not performed on the MeetingDate entities. For example, I add an extra meeting date row and don't enter a value for the date. When I save the committee, I don't get any errors in the Form
, but the exception below occurs:
play.api.Application$$anon$1: Execution exception[[RollbackException: Error while committing the transaction]]
at play.api.Application$class.handleError(Application.scala:296) ~[play_2.11-2.3.4.jar:2.3.4]
at play.api.DefaultApplication.handleError(Application.scala:402) [play_2.11-2.3.4.jar:2.3.4]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3$$anonfun
$applyOrElse$4.apply(PlayDefaultUpstreamHandler.scala:320) [play_2.11-2.3.4.jar:2.3.4]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3$$anonfun
$applyOrElse$4.apply(PlayDefaultUpstreamHandler.scala:320) [play_2.11-2.3.4.jar:2.3.4]
at scala.Option.map(Option.scala:145) [scala-library-2.11.1.jar:na]
Caused by: javax.persistence.RollbackException: Error while committing the transaction
at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:94) ~[hibernate-entitymanager-4.3.5.Final.jar:4.3.5.Final]
at play.db.jpa.JPA$2.apply(JPA.java:193) ~[play-java-jpa_2.11-2.3.4.jar:2.3.4]
at play.core.j.FPromiseHelper$$anonfun$map$1.apply(FPromiseHelper.scala:98) ~[play_2.11-2.3.4.jar:2.3.4]
at scala.util.Success$$anonfun$map$1.apply(Try.scala:236) ~[scala-library-2.11.1.jar:na]
at scala.util.Try$.apply(Try.scala:191) ~[scala-library-2.11.1.jar:na]
Caused by: javax.validation.ConstraintViolationException: Validation failed forclasses [models.MeetingDate] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='error.required', propertyPath=meetingDate, rootBeanClass=class models.MeetingDate, messageTemplate='error.required'}
]
at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:160) ~[hibernate-core-4.3.6.Final.jar:4.3.6.Final]
at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:95) ~[hibernate-core-4.3.6.Final.jar:4.3.6.Final]
at org.hibernate.action.internal.EntityInsertAction.preInsert(EntityInsertAction.java:218) ~[hibernate-core-4.3.6.Final.jar:4.3.6.Final]
at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:97) ~[hibernate-core-4.3.6.Final.jar:4.3.6.Final]
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:463) ~[hibernate-core-4.3.6.Final.jar:4.3.6.Final]
[ERROR] [09/03/2014 23:25:59.167] [play-akka.actor.default-dispatcher-4] [akka.dispatch.Dispatcher] EntityManager is closed
java.lang.IllegalStateException: EntityManager is closed
at org.hibernate.jpa.internal.EntityManagerImpl.checkOpen(EntityManagerImpl.java:105)
at org.hibernate.jpa.internal.EntityManagerImpl.checkOpen(EntityManagerImpl.java:96)
at org.hibernate.jpa.internal.EntityManagerImpl.close(EntityManagerImpl.java:148)
at play.db.jpa.JPA$3.invoke(JPA.java:209)
at play.db.jpa.JPA$3.invoke(JPA.java:203)
at play.core.j.FPromiseHelper$$anonfun$onFailure$1.applyOrElse(FPromiseHelper.scala:116)
at play.core.j.FPromiseHelper$$anonfun$onFailure$1.applyOrElse(FPromiseHelper.scala:116)
at scala.concurrent.Future$$anonfun$onFailure$1.apply(Future.scala:136)
at scala.concurrent.Future$$anonfun$onFailure$1.apply(Future.scala:134)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32)
at play.core.j.HttpExecutionContext$$anon$2.run(HttpExecutionContext.scala:40)
at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:41)
at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:393)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
[error] a.d.Dispatcher - EntityManager is closed
java.lang.IllegalStateException: EntityManager is closed
at org.hibernate.jpa.internal.EntityManagerImpl.checkOpen(EntityManagerImpl.java:105) ~[hibernate-entitymanager-4.3.5.Final.jar:4.3.5.Final]
at org.hibernate.jpa.internal.EntityManagerImpl.checkOpen(EntityManagerImpl.java:96) ~[hibernate-entitymanager-4.3.5.Final.jar:4.3.5.Final]
at org.hibernate.jpa.internal.EntityManagerImpl.close(EntityManagerImpl.java:148) ~[hibernate-entitymanager-4.3.5.Final.jar:4.3.5.Final]
at play.db.jpa.JPA$3.invoke(JPA.java:209) ~[play-java-jpa_2.11-2.3.4.jar:2.3.4]
at play.db.jpa.JPA$3.invoke(JPA.java:203) ~[play-java-jpa_2.11-2.3.4.jar:2.3.4]
The MeetingDate
object is definitely invalid because it's meetingDate
property is null. My question is: why is this error not reported in the form errors, i.e. why is the following expression false?
Form.form(Committee.class).bindFromRequest().hasErrors()
Just figured out what I was missing - I needed to add the
@Valid
annotation to my one-to-many relationship:This triggers the validation on the nested field.