Hibernate Validation latest and greatest

I recently decided to upgrade my Hibernate Validator code and discovered that the framework had changed quite a bit in 4.0.2 to support the new Java validation API. I wanted to share some of my code in the hopes that it would help some of you become familiar with how to use this framework.

The framework itself still pretty much works the same way for annotating your Hibernate classes, it’s the programmatic validation and the custom validators that are different. Let’s look at an example from a Hibernate class. Remember that you are not restricted to using the validation framework with just Hibernate classes. It’s just that with Hibernate annotated classes the validation occurs automatically before the database is hit. You don’t have to manually validate when using Hibernate ORM.

@NotNull //enforce that this cannot be null
@NotEmpty //enforce it cannot be an empty string
@Column(name=”username”, nullable=false)
@Index(name=”IDX_USER_NAME”)
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

Let’s assume that we want to call a validation on this class manually. I added a method to my GenericDAO to accomplish this, but you could do this anywhere you like. I prob should have lazy initialized this, but for the sake of example, I’ll just mention what I should have done.

protected GenericDAO(final Class type) {
this.type = type;

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();

}

//validation
public Set> validate(final T t) {

return validator.validate(t);

}

You can also use the validator in Spring by using the @Valid annotation. Learn more

@RequestMapping(method=RequestMethod.POST)

public String create(@Valid User user, BindingResult result) {
if (result.hasErrors()) {
return "user/createForm";
}
//process adding a user here
//do something
return "redirect:/user/" + user.getId();
}

Now we can use an instance of the UserDAO to validate our object.

@Autowired
UserDAO userDAO;

User user = new User();
user.setUserName(“”);

userDAO.validate(user);

This method will return a Collection of ConstraintViolation so that you can process and do what you want with the result. I created a method in a utility to format and return the result. It’s not elegant, but it works.

public static String formatViolations(Set violations) {

StringBuilder sb = new StringBuilder();
for (ConstraintViolation violation : violations) {

sb.append(“—————————————————-\n”);
sb.append(“Property: ” + violation.getPropertyPath() + “\n”);
sb.append(“Message: ” + violation.getMessage() + “\n”);
sb.append(“Value ” + violation.getInvalidValue() + “\n”);
sb.append(“Entity ” + violation.getLeafBean() + “\n”);
sb.append(“—————————————————-\n”);
}

return sb.toString();

}

That’s all pretty simple and as you could see, this mechanism could work on any class. Remember there are many different annotations for validation, including but not limited to @Size, @Length, @Email etc…

Aside from usual suspects in the out of the box validation implementation, you also have the ability to create your own. This is where I had to rewrite some of my old code to migrate to the latest validations framework.

Let’s look at a validation I created to enforce rules on passwords by a specified strategy. First we have the annotation @Password that we can apply to a password String field of our User object.

@Constraint(validatedBy = PasswordValidator.class)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
public @interface Password {

PasswordStrategy strategy() default PasswordStrategy.NO_STRATEGY;

String message() default “{validator.invalid_password}”;

Class[] groups() default {};

Class[] payload() default {};

}

Now we need the Strategy that we can pass to the annotation to specify which strategy we want to enforce in your application. This class will get ahold of a custom ValidationMessages.properties that is at the root of our classpath to process some messages. It looks complicated, but explaining how it works is beyond the scope of this document.

import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public enum PasswordStrategy {

NO_STRATEGY(“.*”, 0, “validator.invalid_password.NO_STRATEGY”),
DIGIT_STRATEGY(“\\d[0-9]“, 0,”validator.invalid_password.DIGIT_STRATEGY”),
STRONG_STRATEGY(“^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d.*\\d)[a-zA-Z0-9.!@#$%^&*()\\s]{6,30}$”, 0, “validator.invalid_password.STRONG_STRATEGY”),
PASSPHRASE_STRATEGY(“^(?=.*[a-z])(?=.*[A-Z])(?=.*[\\s])[a-zA-Z0-9\\s]{6,30}$”, 0, “validator.invalid_password.PASSPHRASE_STRATEGY”),
USERID_STRATEGY(“^[a-zA-Z0-9.!@#$%^&*()\\s]{6,30}$”, 0, “validator.invalid_userid.USERID_STRATEGY”);

@Override
public String toString() {

return getMessage();
}

PasswordStrategy(String pattern, int flags, String message){
this.pattern = pattern;
this.flags =flags;
this.message = message;

}

private final String pattern;
private final int flags;
private final String message;
private Pattern patternRegEx;

public String getMessage(){

String thisResult = ResourceBundle.getBundle(“ValidationMessages”).getString(message);
return thisResult;

}

public boolean validate(Object value) {
patternRegEx = Pattern.compile(
pattern,
flags );

if ( value == null ) return true;
if ( !( value instanceof String ) ) return false;
String password = (String) value;
Matcher m = patternRegEx.matcher( password );
return m.matches();
}
}

Now we need the Password annotation implemetation.

public class PasswordValidator implements ConstraintValidator {
private PasswordStrategy strategy;

//part of the Validator contract,
//allows to get and use the annotation values
public void initialize(Password parameters) {
strategy = parameters.strategy();
}

public boolean isValid(String value, ConstraintValidatorContext constraintContext) {

return strategy.validate(value);
}

}

Now we can apply our validation to our User class. Note that we chose the STRONG_STRATEGY that will map to the Regular expression in the PasswordStrategy class.

@NotNull
@NotEmpty
@Column(name=”password”, nullable=false, insertable=false, updatable=false)
@Password(strategy=PasswordStrategy.STRONG_STRATEGY)
public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

Now here is our ValidationMessages.properties

validator.invalid_password={strategy}
validator.invalid_password.NO_STRATEGY=The password is invalid
validator.invalid_password.DIGIT_STRATEGY=Your password must be digits
validator.invalid_password.STRONG_STRATEGY=A valid password is a minimum of six characters and a maximum of thirty. It should contains at least one lower case letter, one upper case letter and two digits. Make your password stronger by using a special character (.!@#$%^&*) or by including separate words
validator.invalid_password.PASSPHRASE_STRATEGY=A valid password passphrase is a minimum of six characters and a maximum of thirty. It should contains at least one lower case letter, one upper case letter and contain multiple words
validator.invalid_userid.USERID_STRATEGY=A Valid user id must be a minimum of 6 characters and no more than 30
javax.validation.constraints.AssertFalse.message=must be false
javax.validation.constraints.AssertTrue.message=must be true
javax.validation.constraints.DecimalMax.message=must be less than or equal to {value}
javax.validation.constraints.DecimalMin.message=must be greater than or equal to {value}
javax.validation.constraints.Digits.message=numeric value out of bounds (. expected)
javax.validation.constraints.Future.message=must be in the future
javax.validation.constraints.Max.message=must be less than or equal to {value}
javax.validation.constraints.Min.message=must be greater than or equal to {value}
javax.validation.constraints.NotNull.message=may not be null
javax.validation.constraints.Null.message=must be null
javax.validation.constraints.Past.message=must be in the past
javax.validation.constraints.Pattern.message=must match “{regexp}”
javax.validation.constraints.Size.message=size must be between {min} and {max}
org.hibernate.validator.constraints.Email.message=not a well-formed email address
org.hibernate.validator.constraints.Length.message=length must be between {min} and {max}
org.hibernate.validator.constraints.NotEmpty.message=may not be empty
org.hibernate.validator.constraints.Range.message=must be between {min} and {max}

This file is a copy of the default one included in hibernate itself. I have customized it a bit. I don’t consider localization in this application, but considering multiple language isn’t that difficult using this framework.

The new hibernate 4.0.2 validation mechanism is compliant and inline with the Java Validation Framework defined in JSR303. This was a great step in getting on a common validation framework. If you are starting a new project with Hibernate or just want to use a validation mechanism, Hibernate Validator 4.0.2 or greater is the place to start.

Follow

Get every new post delivered to your Inbox.

Join 28 other followers