This is the fifth in a series of short blogs describing how the Hibernate Validator allows you to define validation rules directly on domain objects. In the fourth article I explained why I don't use several of the standard Hibernate validators. In this article I'll show how to bypass Hibernate validation and when that makes sense.

As indicated by the title of this article, the primary use case I've seen for bypassing standard validation on an object is to allow saving objects in a draft state. For example, suppose you have a lengthy form that a user can fill out, such as a mortgage application or insurance claim, and you want to allow users to save their work as a draft and be able to come back and complete at a later time. In this case, you probably don't want to apply all the validation rules, since it is almost a given that if they haven't finished filling in the form, that it won't pass the validation rules. In this use case bypassing Hibernate validation makes perfect sense. By the way, I am not going to get into the argument of whether storing draft data means that the database must have relaxed constraints, and whether this is good or bad. If you don't relax the database constraints (e.g. allow null values in columns that should be required), then where do you store the draft data? Probably in a separate table that looks almost identical to the "real" table except with all the constraints relaxed (e.g. you might have a loan_applications table as well as a draft_loan_applications table). Assuming, like me, that you don't like that solution and would prefer to save draft objects in the same database table as non-draft objects, read on.

In order to allow the Hibernate event-based validation to be bypassed, you need to create your own validation event listener and provide a way to disable validation temporarily. Since the actual validation logic doesn't change, and the only extra logic we need is to allow clients to turn validation off and then back on, we can extend Hibernate's ValidateEventListener. The following class is all you need (some lengthy JavaDocs have been omitted for brevity):

package com.nearinfinity.common.hibernate.validator.event;

import org.hibernate.event.PreInsertEvent;
import org.hibernate.event.PreUpdateEvent;
import org.hibernate.validator.event.ValidateEventListener;

/**
 * Extension of Hibernate's <code>ValidateEventListener</code> that allows you to bypass the normal validation performed
 * by Hibernate for a specific thread.
 *
 * @author Andrew Avenoso
 * @author Scott Leberknight
 */
public class OptionalValidateEventListener extends ValidateEventListener {

    private static ThreadLocal<Boolean> shouldValidateThreadLocal = new ThreadLocal<Boolean>() {
        @Override
        protected Boolean initialValue() {
            return Boolean.TRUE;
        }
    };

    /**
     * Perform validation before insert, <code>unless</code> {@link #turnValidationOff()} has been called for the
     * currently executing thread.
     *
     * @param event the PreInsertEvent
     * @return Return true if the operation should be vetoed
     */
    @Override
    public boolean onPreInsert(PreInsertEvent event) {
        return isCurrentlyValidating() && super.onPreInsert(event);
    }

    /**
     * Perform validation before update, <code>unless</code> {@link #turnValidationOff()} has been called for the
     * currently executing thread.
     *
     * @param event the PreUpdateEvent
     * @return Return true if the operation should be vetoed
     */
    @Override
    public boolean onPreUpdate(PreUpdateEvent event) {
        return isCurrentlyValidating() && super.onPreUpdate(event);
    }

    /** Call this method to explicitly turn validation on for the currently executing thread. */
    public static void turnValidationOn() {
        OptionalValidateEventListener.shouldValidateThreadLocal.set(Boolean.TRUE);
    }

    /** Call this method to bypass validation for the currently executing thread. */
    public static void turnValidationOff() {
        OptionalValidateEventListener.shouldValidateThreadLocal.set(Boolean.FALSE);
    }

    /** @return <code>true</code> if we need to validate for the current thread */
    public static Boolean isCurrentlyValidating() {
        return OptionalValidateEventListener.shouldValidateThreadLocal.get();
    }
}

The most important things about the above class are:

  • Validation is turned on/off on a per-thread basis using a ThreadLocal variable.
  • If validation is turned off for a specific thread by a client, the client should ensure validation is turned back on after performing persistence operations.

The reason this class uses a ThreadLocal to determine whether to perform validation is because it assumes usage in a multi-threaded environment; for example, you would definitely not want turn turn off validation for all threads in a web application serving lots of concurrent users even for a very short duration! In addition, the reason it is important for clients to reset the validation state for a thread is because the thread might be reused for subsequent client requests, as in many application servers in a JEE environment which pool and reuse threads. (Thanks to Jeff Kunkle for pointing this out.)

The other important thing here has nothing to do with the above code and is instead a configuration issue. If you read my second article in this series you know that Hibernate auto-registers the default ValidateEventListener if it is present in the CLASSPATH, which it will be when you have the Hibernate Validator JAR in your project. So you will need to do two things (refer back to the second article for the configuration details):

  1. Configure the OptionalValidateEventListener for "pre-insert" and "pre-update" events.
  2. Disable automatic registration of the Hibernate ValidateEventListener by setting the hibernate.validator.autoregister_listeners property to "false."

Once that's done you only need to figure out where you need to bypass validation and use code like the following, which is taken from one of our base Spring MVC web controller classes in my current project:

 if (shouldPerformHibernateValidation()) {
     return onSubmitInternal(request, response, command, errors);
 }
 else {
     OptionalValidateEventListener.turnValidationOff();
     try {
         // The following method call would run without any Hibernate validation! 
         return onSubmitInternal(request, response, command, errors);
     }
     finally {
         OptionalValidateEventListener.turnValidationOn();
     }
 }

In the above, the most important thing is that we use a finally block to ensure validation is turned back on regardless of what happened inside the onSubmitInternal() method. We essentially hid this code in a "framework" class so that it is not littered in all the places where we need to bypass validation. The implementation of shouldPerformHibernateValidation() method could be implemented any number of ways, but the point is that you need some way to decide to perform validation or bypass it. Once that decision is made it is easy to turn validation off, execute the code where the bypass occurs, and finally (pun intended) turn validation back on.

In the next and final article in this series, I'll describe how the Hibernate Validator can be integrated into web applications so that validation errors propagate seamlessly from the data access code back up through the web tier and to the end user as nicely formatted error messages.

Validating Domain Objects in Hibernate Part 4: @NotNull and @NotEmpty

Posted on October 05, 2007 by Scott Leberknight

This is the fourth in a series of short blogs describing how the Hibernate Validator allows you to define validation rules directly on domain objects where it belongs. In the third article I showed how to create your own validators. In this article I'll explain the statement I made in the last article that I don't use the @NotNull and @NotEmpty validations in practice, even though at first glance they would seem to be some very useful validators.

First the @NotEmpty validator. Actually this annotation is fine assuming you want to validate "that a String is not empty (not null and length > 0) or that a Collection (or array) is not empty (not null and length > 0)." That is the description in the JavaDoc for the @NotEmpty validator. My only problem with this is that @NotEmpty only applies to strings and collections or arrays. There are lots of times when you want to ensure that dates, numbers, or custom types are required, and @NotEmpty can't help you out. That's pretty much why I don't use it.

Now on to the @NotNull validation annotation. There is a major problem with this validator, which is that it simply doesn't behave the way other validators behave. If you try to save an object having a property annotated with @NotNull, and that property's value is actually null, you would expect to receive an InvalidStateException, which is what happens with other validators. What you actually receive is a PropertyValueException which is the result of Hibernate enforcing a nullability check on the property annotated with @NotNull. I have gone through what happens line-by-line in a debugger and, other than the fact that it is extremely complicated, eventually you arrive at the checkNullability() method in the Nullability class which checks "nullability of the class persister properties" according to the JavaDocs and throws a PropertyValueException with the message "not-null property references a null or transient value." This behavior happens even if the actual column in the database allows nulls!

For example, I have a simple User entity with an active property annotated with @NotNull defined as follows:

 @Type(type = "yes_no")
 @NotNull
 public Boolean getActive() {
     return active;
 }

The user table is defined like this (to show that the active column allows null values):

mysql> desc user;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | bigint(20)   | NO   | PRI | NULL    | auto_increment | 
| version    | bigint(20)   | YES  |     | NULL    |                | 
| active     | char(1)      | YES  |     | NULL    |                | 
| first_name | varchar(255) | YES  |     | NULL    |                | 
| last_name  | varchar(255) | YES  |     | NULL    |                | 
| user_name  | varchar(255) | YES  |     | NULL    |                | 
+------------+--------------+------+-----+---------+----------------+
6 rows in set (0.02 sec)

Finally, I have a test that shows that a PropertyValueException is thrown instead of an InvalidStateException:

@Test(expected = PropertyValueException.class)
public void testNotNullAnnotationPreemptsNormalValidation() {
    // Explicitly set property annotated with @NotNull to null
    user.setActive(null);
    session.save(user);
}

This test passes, meaning that you get a PropertyValueException where with other validators you get an InvalidStateException. For example, here is another test that tests the validation on the username property which is annotated with @Email:

@Test(expected = InvalidStateException.class)
public void testNormalValidationErrorIfNotNullPropertyIsValid() {
    //  Active property is OK here as it gets the default value 'true' in the User class

    // But...make username invalid
    user.setUserName("bob");
    session.save(user);
}

The above test passes meaning that an InvalidStateException was thrown. So, the point of all this long-windedness is that @NotNull behaves differently than other validators and results in a completely different exception being thrown. That is the reason I don't use it and why I created the @Required annotation I showed in the last article.

In the next article, I'll show a technique to bypass validation to allow objects to be saved without the normal validation occurring (and explain a use case where bypassing validation makes sense).