Chapter 2. Using the Validator framework

Hibernate Validator is intended to be used to implement multi-layered data validation, where constraints are expressed in a single place (the annotated domain model) and checked in various different layers of the application.

This chapter will cover Hibernate Validator usage for different layers

2.1. Database schema-level validation

Out of the box, Hibernate Annotations will translate the constraints you have defined for your entities into mapping metadata. For example, if a property of your entity is annotated @NotNull, its columns will be declared as not null in the DDL schema generated by Hibernate.

Using hbm2ddl, domain model constraints will be expressed into the database schema.

If, for some reason, the feature needs to be disabled, set hibernate.validator.apply_to_ddl to false.

2.2. ORM integration

Hibernate Validator integrates with both Hibernate and all pure Java Persistence providers

2.2.1. Hibernate event-based validation

Hibernate Validator has two built-in Hibernate event listeners. Whenever a PreInsertEvent or PreUpdateEvent occurs, the listeners will verify all constraints of the entity instance and throw an exception if any constraint is violated. Basically, objects will be checked before any inserts and before any updates made by Hibernate. This includes changes applied by cascade! This is the most convenient and the easiest way to activate the validation process. On constraint violation, the event will raise a runtime InvalidStateException which contains an array of InvalidValues describing each failure.

If Hibernate Validator is present in the classpath, Hibernate Annotations (or Hibernate EntityManager) will use it transparently. If, for some reason, you want to disable this integration, set hibernate.validator.autoregister_listeners to false

Note

If the beans are not annotated with validation annotations, there is no runtime performance cost.

In case you need to manually set the event listeners for Hibernate Core, use the following configuration in hibernate.cfg.xml:

<hibernate-configuration>
    ...
    <event type="pre-update">
        <listener 
          class="org.hibernate.validator.event.ValidateEventListener"/>
    </event>
    <event type="pre-insert">
        <listener 
          class="org.hibernate.validator.event.ValidateEventListener"/>
    </event>
</hibernate-configuration>

2.2.2. Java Persistence event-based validation

Hibernate Validator is not tied to Hibernate for event based validation: a Java Persistence entity listener is available. Whenever an listened entity is persisted or updated, Hibernate Validator will verify all constraints of the entity instance and throw an exception if any constraint is violated. Basically, objects will be checked before any inserts and before any updates made by the Java Persistence provider. This includes changes applied by cascade! On constraint violation, the event will raise a runtime InvalidStateException which contains an array of InvalidValues describing each failure.

Here is how to make a class validatable:

@Entity
@EntityListeners( JPAValidateListener.class )
public class Submarine {
    ...
}

Note

Compared to the Hibernate event, the Java Persistence listener has two drawbacks. You need to define the entity listener on every validatable entity. The DDL generated by your provider will not reflect the constraints.

2.3. Application-level validation

Hibernate Validator can be applied anywhere in your application code.

ClassValidator personValidator = new ClassValidator( Person.class );
ClassValidator addressValidator = new ClassValidator( Address.class, ResourceBundle.getBundle("messages", Locale.ENGLISH) );

InvalidValue[] validationMessages = addressValidator.getInvalidValues(address);

The first two lines prepare the Hibernate Validator for class checking. The first one relies upon the error messages embedded in Hibernate Validator (see Error messages), the second one uses a resource bundle for these messages. It is considered a good practice to execute these lines once and cache the validator instances.

The third line actually validates the Address instance and returns an array of InvalidValues. Your application logic will then be able to react to the failure.

You can also check a particular property instead of the whole bean. This might be useful for property per property user interaction

ClassValidator addressValidator = new ClassValidator( Address.class, ResourceBundle.getBundle("messages", Locale.ENGLISH) );

//only get city property invalid values
InvalidValue[] validationMessages = addressValidator.getInvalidValues(address, "city");

//only get potential city property invalid values
InvalidValue[] validationMessages = addressValidator.getPotentialInvalidValues("city", "Paris")

2.4. Presentation layer validation

When working with JSF and JBoss Seam™, one can triggers the validation process at the presentation layer using Seam's JSF tags <s:validate> and <s:validateAll/>, letting the constraints be expressed on the model, and the violations presented in the view

<h:form>
    <div>
        <h:messages/>
    </div>
    <s:validateAll>
        <div>
            Country:
            <h:inputText value="#{location.country}" required="true"/>
        </div>
        <div>
            Zip code:
            <h:inputText value="#{location.zip}" required="true"/>
        </div>
        <div>
            <h:commandButton/>
        </div>
    </s:validateAll>
</h:form>

Going even further, and adding Ajax4JSF™ to the loop will bring client side validation with just a couple of additional JSF tags, again without validation definition duplication.

Check the JBoss Seam documentation for more information.

2.5. Validation informations

As a validation information carrier, hibernate provide an array of InvalidValue. Each InvalidValue has a buch of methods describing the individual issues.

getBeanClass() retrieves the failing bean type

getBean()retrieves the failing instance (if any ie not when using getPotentianInvalidValues())

getValue() retrieves the failing value

getMessage() retrieves the proper internationalized error message

getRootBean() retrieves the root bean instance generating the issue (useful in conjunction with @Valid), is null if getPotentianInvalidValues() is used.

getPropertyPath() retrieves the dotted path of the failing property starting from the root bean