NotNull Hibernate Annotation Validation Problems
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
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 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.
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.