bean validation with spring mvc

In this post, you will read about Bean validation feature and how to leverage on this feature using Spring MVC in java web development. For more info, read the complete story here.

Bean validation is one of the feature provided by the JSR 303 specification in Java EE using annotations. JPA 2.0 provides some default field level constraints, some of them are @NotNull,@Email,@Size,@Future,@past,@pattern etc.

Writing Custom Constraints:

We can custom annotation and provide custom logic using ConstraintValidator class provided by Spring framework

Here is the Custom Constraint Annotations:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
@Documented
@Constraint(validatedBy = MobileNumberValidator.class)
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface MobileNumber {
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

Here is custom Constraint Validator:

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class MobileNumberValidator implements ConstraintValidator<MobileNumber, String>{
@Override
public void initialize(MobileNumber number) {
}
@Override
public boolean isValid(String mobileNumber, ConstraintValidatorContext ctx) {
if(mobileNumber == null){
return false;
}
if (mobileNumber.matches("\\d{10}")) return true;
else if(mobileNumber.matches("\\d{3}[-\\.\\s]\\d{3}[-\\.\\s]\\d{4}")) return true;
else if(mobileNumber.matches("\\d{3}-\\d{3}-\\d{4}\\s(x|(ext))\\d{3,5}")) return true;
else if(mobileNumber.matches("\\(\\d{3}\\)-\\d{3}-\\d{4}")) return true;
else return false;
}
}
@Constraint(validatedBy = MobileNumberValidator.class)

Mentioned line is our custom constraint and it will validated by MobileNumberValidator class.

@MobileNumber(groups = FormTwo.class)

The above line need to mention in entity class(s) where you want to validate the input data.

Sample entity will look like this:

public class User {
public interface FormOne {
}
public interface FormTwo {
}
@NotBlank(groups = FormOne.class)
private String firstname;
@NotBlank(groups = FormOne.class)
private String lastname;
@NotBlank(groups = FormOne.class)
private String email;
@Min(value=18,groups = FormOne.class,message="${validatedValue} is not greater than {value}")
@Max(value=100,groups = FormOne.class)
private int age;
@NotBlank(groups = FormOne.class)
private String gender;
// fields to validate in form two
@NotBlank(groups = FormTwo.class)
private String state;
@NotBlank(groups = FormTwo.class)
private String city;
@NotBlank(groups = FormTwo.class)
private String country;
@NotBlank(groups = FormTwo.class)
@MobileNumber(groups = FormTwo.class)
private String mobileNumber;
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;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobileNumber() {
return mobileNumber;
}
public void setMobileNumber(String mobileNumber) {
this.mobileNumber = mobileNumber;
}
}

The above entity contains not only default constraints provided by JPA2.0 and also we just created custom constraint.

@Min, @Max are the constraint provided by JPA, and @NotBlank is the Constraint provided by Hibernate.

Spring MVC comes with generic validator to validate all entities.

Here is spring configuration required to validate the entities.

<beans:bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:property name="basename" value="classpath:message" />
<beans:property name="defaultEncoding" value="UTF-8" />
</beans:bean>
<beans:bean id="validator"  class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<beans:property name="validationMessageSource" ref="messageSource"/>
</beans:bean>

Where ReloadableResourceBundleMessageSource bean contains the message properties which can be useful for internationalization.

LocalValidatorFactoryBean is the generic class come with spring MVC.

ReloadableResourceBundleMessageSource  bean must be passed to LocalValidatorFactoryBean  to return the internationalized error message if the value is not satisfied according to the constraint.

Spring 4 comes with support of BeanValidation 1.1 using Hibernate validator 5.

Some of the Added Features in BeanValidation 1.1

For each constraint we can mention Group attribute which says that if the validator can validate the field if and only if the value attribute of the validator has group value.

@RequestMapping(value = "/formOne", method = RequestMethod.GET)
public String saveFormOne(Model model) {
logger.info("Returning formOne.jsp page");
model.addAttribute("user", new User());
return "formOne";
}

We are specifying here validator can validate only FormOne group, means the entity whose fields are mapped to FormOne will validate.

Here we can pass list of different classes to validator, if any one of configured group is matches then it will validate otherwise it will not.

Validated is the annotation provided by spring Framework on top of JSR-303 provided annotation (@Valid).

BindindResult is the object which will hold the all errormessages which is(are) thrown by ConstraintValidator.

Using Binding we can show the error messages to user.

Here is the jsp which will do this:

<springForm:form method="POST" commandName="user" action="/Bean-Validation/formOne">
<table>
<tr>
<td>First Name:</td>
<td><springForm:input path="firstname" /></td>
<td><springForm:errors path="firstname" cssClass="error" /></td>
</tr>
<tr>
<td>Last Name:</td>
<td><springForm:input path="lastname" /></td>
<td><springForm:errors path="lastname" cssClass="error" /></td>
</tr>
<tr>
<td>Email:</td>
<td><springForm:input path="email" /></td>
<td><springForm:errors path="email" cssClass="error" /></td>
</tr>
<tr>
<td>Age:</td>
<td><springForm:input path="age" /></td>
<td><springForm:errors path="age" cssClass="error" /></td>
</tr>
<tr>
<td>Gender:</td>
<td><springForm:select path="gender">
<springForm:option value="" label="Select Gender" />
<springForm:option value="MALE" label="Male" />
<springForm:option value="FEMALE" label="Female" />
</springForm:select></td>
<td><springForm:errors path="gender" cssClass="error" /></td>
</tr>
<tr>
<td colspan="3"><input type="submit" value="Save"></td>
</tr>
</table>
</springForm:form>

Where springForm is the spring MVC taglib we are using here, which comes by spring MVC.

SpringForm error tag hold all the error messages thrown by the constraint Validator.

Using path attribute of error tag we can show the error message next to field.

show error message using path attribute

These messages can customized using ReloadableResourceBundleMessageSource class.

But the problem here is if we want to show the user entered input in error messages, Bean vlidation 1.0 will not support, but Beanvalidation 1.1 provides solution using new MessageInterPolator.

The new MessageInterpolator added new variable validatedValue which holds the entity field value, which can be used in error message formation.

Example:

@Min(value=18,groups = FormOne.class,message="${validatedValue} is not greater than {value}")
@Max(value=100,groups = FormOne.class)
private int age;

Here age field will allow the values from 18 to 100, and we are using ${validatedValue} to show the user input value in error message.

show user input value on error message

Sample project to explain all these features:

https://github.com/sravan4rmhyd/SpringMVCBeanValidation.git

Steps to import project into STS:

import project into STS

Provide the git url to clone the project

provide git url clone script

import project source git repository

Select the master branch to clone the project

select master branch clone project

Provide the local destination folder.

provide local destination folder

select wizard import projects

import projects from git repository

spring mvc bean validation clone

Steps to run the project:

I) run the project as Maven build

II) provide in the goal : tomcat7:run

run project as maven build

provide in the goal tomcat7 run

Once run the project in console it will displays the context-path and port number on which it is running.

project console display context path and port number

Url for rendering formOne page:

url rendering form one page

Java web development team has just shared a post regarding leveraging BeanValidation feature using Spring MVC. If there is something that we skipped, please tell. Share your experience with other readers and tell how you find this story.

James Warner is a highly qualified digital marketing and entrepreneurship. I’m a contributing editor of NexSoftsys for many years in a various role including editor Technology, Health & Media editor and also working as a freelance. You can contact me on Facebook or Follow me on Twitter or add your circle Google+

Leave a Comment

Your email address will not be published. Required fields are marked *