Saturday, April 2, 2011

Force your beans validation using JSR 303 and Hibernate Validator 4.1.0

In my list of most loved features JSR 303 is somewhere among top ones. Not only because it's extremely useful, but also because it has simple design and extensibility. Hibernate project actively uses validation techniques for a long time as part of Hibernate Validator subproject, recently with JSR 303 implementation support.

Today I would like to share some basic uses cases of JSR 303 annotations and programmatic validation support. Let us start with first one.

1. Assume you are expecting Person object to be passed to you business logic layer, just typical Java bean with few properties.
package org.example;

public class Person {
    private String firstName;
    private String lastName;
    
    public void setFirstName( String firstName ) {
        this.firstName = firstName;
    }
 
    public String getFirstName() {
        return firstName;
    }

    public void setLastName( String lastName ) {
        this.lastName = lastName;
    }

    public String getLastName() {
        return lastName;
    }   
}
Further, let us assume that according to business logic firstName and lastName properties are required. There are many ways to enforce such kind of constraints but we will use JSR 303 bean validation and Hibernate Validator.
package org.example;

import org.hibernate.validator.constraints.NotBlank;

public class Person {
    @NotBlank private String firstName;
    @NotBlank private String lastName;
    
    ...  
}
The question arises who will actually perform validation? The most straightforward solution is to do it using utility class.
package org.example;

import java.util.HashSet;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.groups.Default;

public class ValidatorUtil {
    private final ValidatorFactory factory;
 
    public ValidatorUtil() {
        factory = Validation.buildDefaultValidatorFactory();
    }
  
    public< T > void validate( final T instance ) {  
        final Validator validator = factory.getValidator();
  
        final Set< ConstraintViolation< T > > violations = validator
            .validate( instance, Default.class );

        if( !violations.isEmpty() ) {
            final Set< ConstraintViolation< ? > > constraints = 
                new HashSet< ConstraintViolation< ? > >( violations.size() );

            for ( final ConstraintViolation< ? > violation: violations ) {
                constraints.add( violation );
            }
   
            throw new ConstraintViolationException( constraints );
        }
    }
}
Having such a class validation is easy:
final Person person = new Person();
new ValidatorUtil().validate( person );
ConstraintViolationException exception will be thrown in case of any validation errors. Let us move on to more complicated examples.

2. Assume you are expecting Department object to be passed to you business logic containing at least one (valid!) person. Let me skip the details and present the final solution here.
package org.example;

import java.util.Collection;

import javax.validation.Valid;

import org.hibernate.validator.constraints.NotEmpty;

public class Department {
    @NotEmpty @Valid private Collection< Person > persons;

    public void setPersons( Collection< Person > persons ) {
        this.persons = persons;
    }

    public Collection< Person > getPersons() {
        return persons;
    }
}
The validation above ensures that Department contains at least one Person instance and each Person's instance is itself valid.

There are just a lot of other use case out there which Hibernate Validator is able to handle out of the box: @Min, @Man, @Email, @Url, @Range, @Length, @Pattern, ... This post is just a starting point ...

10 comments:

Mikko said...

Hello Andriy!

I am wondering if you can use validation with annotation (like @Valid for a method parameter) and not explicitly using a separate ValidatorUtil class. Do you think this could be possible without creating a custom annotation for that?

Cheers!

Andriy Redko said...

Hello Mikko!

You could get rid of ValidatorUtil and delegate validation to f.e. CDI framework or Hibernate (for persistent entities), or do it using AOP, etc. Also, latest Hibernate Validator 4.2.0.Final has method level validation (http://in.relation.to/Bloggers/HibernateValidator420Final) and other nice features. :)

Regards!

brokenpipe said...

Hello Andriy,
great usefull post! I'm never to java and i would ask you : how can i catch message exception after " new ValidatorUtil().validate(_articolo); " ?

Could you give an example ?

Thanks

Andriy Redko said...

Hi! Thanks a lot for your comments. Here is a pretty simple example which illustrates the basic approach to handle validation exceptions:
try {
new ValidatorUtil().validate(_articolo);
} catch( ConstraintViolationException ex ) {
// Here you have violated constraints in ex.getConstraintViolations(), just iterate through them
}
The other aproach would be just return the list of violated constraints without throwing the exception. Hope it helps!

brokenpipe said...

yes i tried to cath with try-catch block statement, but ex don't expose getConstraintViolations() but only a getConstraintName() method that return a String type.

Andriy Redko said...

Got it, how about this one (sorry for messy markup):

try {
new ValidatorUtil().validate( new Person() );
} catch( ConstraintViolationException ex ) {
for( final ConstraintViolation< ? > violation: ex.getConstraintViolations() ) {
System.out.println( String.format( "Instance: %s, property: %s", violation.getRootBean(),
violation.getPropertyPath() ) );
}
}

For my example, I have following output:

Instance: org.example.Person@583e0ebb, property: firstName
Instance: org.example.Person@583e0ebb, property: lastName

Andriy Redko said...

Also, if this code doesn't compile, it might be related to the version of Hibernate Validator and/or Validation API you are using.

Andriy Redko said...

Sometimes exception is not the best choice, so you as well could consider to return list of violated constraints from the the validate() method. It's very simple refactoring you can perform.

Senthil Muthiah said...

Good Stuff. Using this examples, i created my own and posted in my blog

Andriy Redko said...

Thanks a lot, I am glad you found this stuff useful!