JSR 303 - Bean Validation with OpenJPA - Part I

on July 25, 2011
    
JSR 303:
JSR-303 is a specification for Bean Validation. It is an attempt to come up with a standard declarative validation framework. The infrastructure defined by JSR-303 enables you to describe the constraints using constraint annotations on your bean and validate your bean using constraint validators.

OpenJPA:
A new feature defined by the JPA 2.0 specification is the ability to integrate with JSR-303 bean validation provider. With minimal effort, OpenJPA 2.0 can be coupled with JSR-303 validation provider to provide runtime data validation. By combining these two technologies, you get a standardized persistence solution with the added ability to perform standardized java bean validation.

What is Bean Validation?
Most applications, especially those that gather input from a user, contain a significant amount of code to perform data validation. It is typically custom code, fragmented and littered throughout the application. Worse, there may be duplicate validation code at the presentation (Web) , business (EJB), and persistence layers. The task of keeping duplicate code in sync can be especially problematic. A slight modification in the code at one layer can lead to an unforeseen breakage in another.

The Bean Validation API was designed to provide a standardized method for validating data within Java beans. As an added feature, it seamlessly integrates with several JEE6 technologies including JPA 2.0, JCA 1.6, and JSF 2.0. Additionally, JEE6 complaint servers are required to support bean validation and must include a validation provider. While a JEE6 environment provides some simplified packaging and configuration benefits, bean validation works equally well in a JSE environment.

While the JSR-303 specification is very feature rich and extensible, there are three core concepts that will be of most interest to the majority of users: constraints, constraint violation handling, and the validator itself. Constraints are a fundamental component of bean validation. Constraints can be placed on Java beans and/or fields(attributes) to constrain the data within the bean. A constraint can either be defined using annotations or XML. The bean validation specification defines a small set of constraints(Built-in constraints) that must be included with every validation provider. This small set covers most types of simple validation in addition to a powerful regular expression based validator. If the built-in constraints don't fit the bill, you can very easily build your own custom validators and constraints. Let's start by looking at some simple constraints and then move to creating some custom constraints.

Pre-requisites:
Before going to the examples, download the required jar file from here. At the time of writing this article, the latest version is 2.1.0. Download the apache-openjpa-2.1.0.zip file, extract it, and copy the openjpa-all-2.1.0.jar file and include it in your classpath.

How to use Built-in Constraints:
In the following example, I am going to explain how to use Built-in Constrains defined by the bean validation specification.

Create a java bean class Book.java and define some simple built-in constraints as shown below,
package com.sample.bean;

import java.util.Date;
import javax.validation.constraints.Future;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

public class Book {

 @NotNull(message="Book Name should not be null")
 @Size(min = 2, max = 50, message = "Name length should be between 2 and 50")
 private String name;
 
 @NotNull(message="Book Author should not be null")
 private String author;
 
 @NotNull(message="Book Edition should not be null")
 private String edition;
 
 @NotNull(message="Release date should not be null")
 @Future(message = "Release date should not be past date")
 private Date releaseDate;

 @Min(1)
 @Max(5)
 private int rating;
 
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getAuthor() {
  return author;
 }
 public void setAuthor(String author) {
  this.author = author;
 }
 public String getEdition() {
  return edition;
 }
 public void setEdition(String edition) {
  this.edition = edition;
 }
 public Date getReleaseDate() {
  return releaseDate;
 }
 public void setReleaseDate(Date releaseDate) {
  this.releaseDate = releaseDate;
 }
 public int getRating() {
  return rating;
 }
 public void setRating(int rating) {
  this.rating = rating;
 } 
}
NOTE: It is possible to put the annotations on the fields or on the setter methods of the bean properties. Personally, I like putting them on the fields as it's easier to pick up when reading the code.
 
Built-In Bean Validation Constraints:

Constraint Description Example
@AssertFalse The value of the field or property must be false.
@AssertFalse
boolean isUnsupported;
@AssertTrue The value of the field or property must be true.
@AssertTrue
boolean isActive;
@DecimalMax The value of the field or property must be a decimal value lower than or equal to the number in the value element.
@DecimalMax("30.00")
BigDecimal discount;
@DecimalMin The value of the field or property must be a decimal value greater than or equal to the number in the value element.
@DecimalMin("5.00")
BigDecimal discount;
@Digits The value of the field or property must be a number within a specified range. The integer element specifies the maximum integral digits for the number, and the fraction element specifies the maximum fractional digits for the number.
@Digits(integer=6, fraction=2)
BigDecimal price;
@Future The value of the field or property must be a date in the future.
@Future
Date eventDate;
@Max The value of the field or property must be an integer value lower than or equal to the number in the value element.
@Max(10)
int quantity;
@Min The value of the field or property must be an integer value greater than or equal to the number in the value element.
@Min(5)
int quantity;
@NotNull The value of the field or property must not be null.
@NotNull
String username;
@Null The value of the field or property must be null.
@Null
String unusedString;
@Past The value of the field or property must be a date in the past.
@Past
Date birthday;
@Pattern The value of the field or property must match the regular expression defined in the regexp element.
@Pattern(regexp="\\(\\d{3}\\)\\d{3}-\\d{4}")
String phoneNumber;
@Size The size of the field or property is evaluated and must match the specified boundaries. If the field or property is a String, the size of the string is evaluated. If the field or property is a Collection, the size of the Collection is evaluated. If the field or property is a Map, the size of the Map is evaluated. If the field or property is an array, the size of the array is evaluated. Use one of the optional max or min elements to specify the boundaries.
@Size(min=2, max=240)
String briefMessage;


Create a java class BookTest.java to test the constraints defined in java bean as shown below,
package com.bean.test;

import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import com.sample.bean.Book;

public class BookTest {
 
 private static Validator validator;

 public static void main(String args[]) {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
        Calendar cal = Calendar.getInstance();
        Book book = new Book();
        //book.setAuthor("Satish");
        //book.setEdition("first");
        //book.setName("java");
        //book.setReleaseDate(new Date("07/21/2011"));
        book.setRating(1);
        Set<ConstraintViolation<Book>> constraintViolations=validator.validate(book);
        int count = constraintViolations.size();
        System.out.println("Total voilations: "+count);        
        Iterator it = constraintViolations.iterator();
        while(it.hasNext()){
           ConstraintViolation cv=(ConstraintViolation)it.next();
           System.out.println(cv.getMessage());
        }        
    }
}
On line 19, we get a Validator object from the ValidatorFactory, we use this validator to validate the 'book' object. The validate() method returns a set of ConstraintViolation objects, which we can iterate through in order to see which validation errors occured.

In the next article, we will see how to create our own custom constraints.
       

0 comments:

Post a Comment