Thursday, June 5, 2014

Using Java Bean Validation (JSR-303)

Java Bean Validation is one of the new validation model in Java 6. This is under Java Specification Requests - 303. According to this specification Java provides an API for JavaBeans Validation. The Bean validation is just like a constraints in the form of annotations placed on fields, methods and class.

To maintain the data integrity is the responsibility of the application logic. So, to maintain the data integrity we have to process the user input before going into the business logic for further processing on the data entered. Before JSR-303, we have to write code to validate one-by-one each of the user input. So, we have to write lots of if..else statements in the code. Sometime it is bigger than the business logic and sometime is very hard to maintain.

So, Java 6 came up with a standardise validation rule API and validation rule engine. Nowadays in the market you can found couple of implementations of Java Bean Validation API (JSR-303). One of the most popular is Apache BVal, there are other also like Hibernate Validator , Spring 3 Validation 

Java standard bean validation API have some standard annotation, those I will be discussing in this post. But this is not the end. We have to look into the implementor's provided extra annotations. Sometime, we can get lots of help from those during the development work.

Standard API provides the following annotations for bean validation placed on fields, methods and class.

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 minelements to specify the boundaries.
@Size(min=2, max=240)
String briefMessage;

Example:I am using MVN to build a project and manage project dependencies.

Step:1
In the command prompt type the command below:

mvn archetype:generate -DgroupId=org.wiki -DartifactId=MyBeanValidator -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Above statement will create a project in the current directory and put the POM file inside that directory.

I am using eclipse, so I have to tell maven to treat above project as a eclipse project. From the command prompt, go inside the directory and type the following.

mvn eclipse:eclipse

Now, you can import this project into the eclipse work space.

Step:2
Now you have to modify POM file to declare the dependency to the java bean validation API. One more thing is - I am using the Apache BVal as an implementation of the JSR-303


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.wiki</groupId>
  <artifactId>MyBeanValidator</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>MyBeanValidator</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>1.1.0.Final</version>
    </dependency>
    <dependency>
    <groupId>org.apache.bval</groupId>
    <artifactId>org.apache.bval.bundle</artifactId>
    <version>0.5</version>
    </dependency>
  </dependencies>

</project>

Step:3

In the command prompt type the following:

mvn compile

above command download dependency and put into the maven local repository.

Step:4
package org.wiki;


import java.math.BigInteger;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;

import org.apache.bval.constraints.Email;
import org.apache.bval.constraints.NotEmpty;

public class Employee {
private BigInteger ID;
private String firstName;
private String lastName;
private String email;
private String phone;
@NotNull(message="ID can not be null.")
public BigInteger getID() {
return ID;
}
public void setID(BigInteger iD) {
ID = iD;
}
@NotNull(message = "First name is compulsory")
@Pattern(regexp = "[a-z-A-Z]*", message = "First name has invalid characters")
@NotEmpty(message="can not be blank.")
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Email(message="Please enter valid email address.")
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Pattern(regexp="[d(3)-d(7)]", message="Please enter a valid phone number")
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}

}

Step:5

package org.wiki;

import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

public class TestBeanValidation 
{
    public static void main( String[] args )
    {
        Employee emp = new Employee();
        emp.setEmail("prabir");
        emp.setFirstName("prabir123");
        
    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        
        Set> violation = validator.validate(emp);
        
        for(ConstraintViolation a : violation)
        {
        System.out.println(a.getMessage());
        }
    }
}


Output:


Please enter valid email address.
First name has invalid characters
ID can not be null.

Enjoy! Java Bean Validation.

Please check my next blog post on Using Apache BVal's extra annotations for bean validation.