NotNull Hibernate Annotation Validation Problems

| | Comments (3) | TrackBacks (0)

I recently decided to try out Hibernate's annotation-based validation framework. It seems like a great solution for ensuring consistent validation of the domain model regardless of the presentation tier (web interface, web service, etc.). It very much reminds me of the declarative validation available within Rails model objects.

To make a long story short, things did not go as well as I had hoped using a Hibernate Core 3.2.0 CR2 and Hibernate Annotations 3.2.0 CR1 combination. I struggled for hours trying to figure out why I was getting a "not-null property references a null or transient value" error rather than a validation error when attempting to use the NotNull validation annotation. Eventually I discovered the problem was not that the NotNull validation wasn't working properly, but that it wasn't running at all.

In order for the annotation-based validation framework to execute, Hibernate Annotations provides a ValidatePreInsertEventListener and ValidatePreUpdateEventListener to fire the annotation validations prior to persisting any changes to the domain model. "Pre-insert" and "pre-update" events are fired after other events, in this case SaveOrUpdateEvent(s). By default, Hibernate uses the DefaultSaveOrUpdateEventListener, which is an extension of AbstractSaveEventListener. The AbstractSaveEventListener checks for null values corresponding to non-nullable database columns ( AbstractSaveEventListener:284 ) before adding the insert or update events to the execution queue( AbstractSaveEventListener:290 ) responsible for triggering pre-insert or pre-update event listeners. In case that didn't make complete sense, the bottom line is the Hibernate Core checks entities against the null constraints of the database before the annotation-based validation framework has a chance to run.

Luckily, there is a workaround, although it feels like a bit of a hack. As I mentioned previously, the DefaultSaveOrUpdateEventListener gets a chance to run before any "pre-insert" or "pre-update" events so we can use that to our advantage. The following SaveOrUpdateEventListener simply extends the Hibernate Annotations-provided ValidateEventListener and calls the validation method on "save-update" events rather than "pre-insert" and "pre-update" events.

public class ValidationEventListener 
    extends ValidateEventListener 
    implements SaveOrUpdateEventListener {

  public void onSaveOrUpdate(SaveOrUpdateEvent event) throws HibernateException {
    final SessionImplementor source = event.getSession();
    final Object object = event.getObject();
    if (!source.getPersistenceContext().reassociateIfUninitializedProxy(object)) {
      // only validate the entity if it has been changed
      final Object entity = 
        source.getPersistenceContext().unproxyAndReassociate(object);
      super.validate(entity, event.getSession().getEntityMode());
    }
  }

}

One extemely important thing to remember when using this workaround is that you must configure the DefaultSaveOrUpdateEventListener as the second "save-update" listener (following the ValidationEventListener) or your objects will never be persisted.

3 Comments

Celso Barreto said:

For me it only worked after configuring the ValidationEventListener with both the save-update and save.

Thanks a lot for the workaround, it saved the day!

Unfortunately your solution is broken. It does not handle entities made persistent by cascade.

Jeff Kunkle said:

Thank you both for letting me know about the save and cascade problems. I had not yet run into them.

Leave a comment


Type the characters you see in the picture above.

0 TrackBacks

Listed below are links to blogs that reference this entry: NotNull Hibernate Annotation Validation Problems.

TrackBack URL for this entry: http://www.nearinfinity.com/mt/mt-tb.cgi/485