Integrating Spring MVC 3.0 with JSR 303 (aka javax.validation.*)

Annotated POJO validation comes to a JDK near you!

The new annotated validation spec (jsr 303) is pretty slick, especially when used along side Spring MVC 3.0, and when backed by ejb3 entities. I’m pretty impressed with how easily it integrates with Spring MVC’s framework, and with how seamlessly error messages are passed to the form taglibs so they show up in your web forms.

I know some of you might argue that the current validation framework might not address complex validations, but after giving Hibernate’s reference implementation documentation a look, it seems interdependent validations are at least possible through embedded @Valid in complex objects. Even if you have to come up with your own really weird validation for a particular field, jsr 303/hibernate offers a way to create your own custom annotation driven validations. For the remaining 95% of all the other web forms, you’re probably going to be alright if you use the pre-defined validations offered by jsr 303.

Getting started

Download the jsr 303 reference implementation jars from SourceForge, via Hibernate’s download page. You’ll need to add the main, Hibernate validator jar (currently hibernate-validator-4.0.2.GA.jar as of 2/8/2009) and the included jars in the release jar’s lib directory to your application’s classpath if they’re not already there (if you’re on jboss 5.1, probably at least validation-api-1.0.0.GA.jar, maybe more). The Hibernate reference implementation release also includes the jar files required to run in a jdk 5 runtime, include those if you’re not running on jdk 6. Download Spring MVC from Spring’s download page, its part of the Spring 3.0 release. Spring MVC requires the following jars in your classpath:

Now that we’ve squared that away, on with the examples:

Example ejb3 jsr-303 validation equipped entity bean

import javax.persistence.*;

@Entity
@Table(name="tb_inventory")
public class InventoryItem implements Serializable {

	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	protected int id;

	@NotNull
	protected BigDecimal price = new BigDecimal("0.00");	

	@NotEmpty(message = "Name is a required field")
	protected String name;

	@Min(100)
	protected int minimumPrice;

	@Email
	private String mail;

	@Pattern(regexp="[a-z]+")
	private String lowerCaseName;

	@Future
	private Date futureDate;

	@AssertTrue
	private boolean mustBeTrue;

	@OneToMany
	@JoinTable(name="tb_order_items_to_images")
	@Valid
	protected List<InventoryImage> images = new ArrayList<InventoryImage>();

	// setters/getters here...
}

Lets examine the annotations:

@NotNull

@NotNull flags the annotated field as valid only if it has a value assigned. A String with a null value will fail, while a String with a “” value will pass. Since the “message” parameter is not defined, the error message will default to whatever the validation package ships with. In this case, I think the message will read “name is a required field”.

@NotEmpty(message = “Name is a required field”)

@NotEmpty flags the annotated field as valid if the field is both not null and not a value of “”. Since the “message” parameter is defined for this annotation, the param value will replace the default message passed into spring mvc’s error framework.

@Min(100)

@Min flags the field as valid only if it has a value equal to or higher than the value in the parens. The contrary to this is @Max, which will flag as valid values lower than the value in the parens.

@Email

@Email flags the field as valid only if the field is a valid email.

@Pattern(regexp=”[a-z]+”)

@Pattern will flag the field as valid only if the string matches the regular expression passed in as the parameter. In this case it will only pass if the string is made up only of lowercase letters.

@Future

@Future will flag as valid only if the date annotated is in the future. The contrary to this is @Past, which would be valid only if the date has already passed.

@AssertTrue

@AssertTrue will flag as valid if the annotated boolean resolves to true. The contrary to this is @AssertFalse, which will flag as valid only if the boolean resolves to false.

@Valid

@Valid will validate only if the complex object annotated validates as true. Lets say in this case that InventoryImage has two validation annotated fields; if any InventoryImage fails either of those two fields, then the enclosing InventoryItem will fail validation because of the @Valid annotation. This is how compelx cross object validations are supported, other than defining your own.

Now that we’ve annotated our bean, we’ll need to hook it into a Spring MVC controller.

The Spring MVC Controller

package com.faceroller.web;

@Controller
@RequestMapping("/inventory")
public class InventoryController {

	private static Log log = LogFactory.getLog(InventoryController.class);

	/**
	 * initialize the form
	 *
	 */
	@RequestMapping("/add/setup.go")
	public ModelAndView addInventorySetup(InventoryItem item){

		log.info("setup add inventory");
		return new ModelAndView("/inventory/add.jsp", "item", item);
	}

	/**
	 * process the form
	 *
	 */
	@RequestMapping(value="/add/meta.go", method=RequestMethod.POST)
	public String processInventoryMeta(
			@ModelAttribute("item") @Valid InventoryItem item,
			BindingResult result) {

		log.info("process add inventory");

		if (result.hasErrors()) {
			return "/inventory/add.jsp";
		}

		InventoryService service = ServiceLocator.getInventoryService();
		service.addInventoryItem(item);

		return "redirect:/add/images/"+item.getId();
	}

	/**
	 * forward to whatever page you want
	 *
	 */
	@RequestMapping("/browse/item/{itemId}")
	public ModelAndView getInventoryItem(@PathVariable int itemId){

		log.info("getting item");

		InventoryService service = ServiceLocator.getInventoryService();
		InventoryItem item = service.getInventoryItemById(itemId);

		return new ModelAndView("/inventory/browse.item.jsp", "item", item);
	}

}

Pay special attention to

	@RequestMapping(value = "/add/meta.go", method=RequestMethod.POST)
	public String processInventoryMeta(
			@ModelAttribute("item") @Valid InventoryItem item,
			BindingResult result)

You’ll notice @Valid marked up right before the InventoryItem item bean parameter. This is the annotation that does all the validation magic for us. There is no need to implement a custom validator factory, as spring mvc’s framework would normally require. If the bean fails validation, BindingResult result will be prepopulated with all corresponding JSR 303 validation errors. The catch is you have to add the @ModelAttribute(“item”) annotation to the signature, otherwise the form bean in the jsp will not have access to all the error messages passed along by the validations.

The jsp code

<form:form method="post" commandName="item" action="/process/form">
<table width="100%" border="0">
	<tr><td colspan="3" class="bottomPadding">
		<span class="secionHeader">Add item to inventory</span>
	</td></tr>
	<tr><td class="labelColumn" width="100">
		Price
	</td><td width="100">
		<form:input path="price"/>
	</td><td>
		<form:errors path="price" cssClass="error"/>
	</td></tr>
	<tr><td class="labelColumn">
		Name
	</td><td>
		<form:input path="name"/>
	</td><td>
		<form:errors path="name" cssClass="error"/>
	</td></tr>
</table>
</form:form>

This is just a simple form, nothing new here, but I’m including for completeness. The Spring MVC framework will correctly populate the form tags with any bean errors should the form fail validation. The form tags are part of the standard spring taglibs, found in the org.springframework.web.servlet.* jar included in the Spring 3.0 distribution.

EDIT:
Stuart Gunter pointed out in a comment to this post that there is a workaround for injecting your own messages using spring’s autowiring. Click the jump for “>his example.

Resources
Hibernate 4.x Validation reference implementation
Spring MVC 3.0 documentation

 
    Twitter
  • del.icio.us
  • Reddit
  • Technorati
  • Google Bookmarks
  • Blogplay
  • Yahoo! Buzz
  • LinkedIn
  • Facebook
  • Digg

Related posts:

  1. Java, XML and XStream
  2. Sending Attachments with the Javamail 1.4.x API
  3. Ejb3 Basics: Deploying Message Driven Beans
Posted on February 8, 2010 at 12:35 am by Ant · Permalink
In: Java · Tagged with: , , , , ,

25 Responses

  1. craig Written by craig
    on February 12, 2010 at 6:41 am
    Permalink

    nice, thanks.

    [Reply]

  2. uberVU - social comments Written by uberVU - social comments
    on February 14, 2010 at 3:07 pm
    Permalink

    Social comments and analytics for this post…

    This post was mentioned on Twitter by gridlockd: “Spring MVC 3.0 and JSR 303 (aka javax.validation.*)” http://is.gd/8d88u #Java #Spring…

  3. [...] Technobabble » Spring MVC 3.0 and JSR 303 (aka javax.validation.*) – The new annotated validation spec (jsr 303) is pretty slick, especially when used along side Spring MVC 3.0, and when backed by ejb3 entities. I’m pretty impressed with how easily it integrates with Spring MVC’s framework, and with how seamlessly error messages are passed to the form taglibs so they show up in your web forms. [...]

  4. Todd Written by Todd
    on February 15, 2010 at 4:28 am
    Permalink

    Thanks for the example. It’s great. Just an FYI, I couldn’t get this to work on Google App Engine until after I upgraded to 1.3.1. Not sure if it was user error, but my error messages started popping up after that.

    [Reply]

    Ant

    Ant Reply:

    Hi Todd, thanks for the comment. Yeah – it seems that Google Apps Engine does something funky with form beans as of spring 2.5.x behind closed doors. I found this google support thread that discusses that specific problem in more detail. I can’t speak with authority but it would appear from your experience that GAE 1.3.1 might address it. I’m glad it wound up working for you, I really like how the two mesh together.

    [Reply]

  5. Karthik Written by Karthik
    on February 17, 2010 at 7:55 pm
    Permalink

    I see an issue with Spring MVC using @Valid annotation though since you cannot specify the JSR 303 validation ‘group’ that you want to validate. Consider the case of a wizard like flow where the same model object is populated across several steps. Specifying @Valid will trigger the validation on default group and some of the fields might not be relevant to a particular step in the wizard like workflow. and I don’t want to use Spring web flow :)

    [Reply]

    Ant

    Ant Reply:

    That’s an interesting case. I’m going to try and look at this and see what I can come up with.

    [Reply]

  6. chris marx Written by chris marx
    on April 15, 2010 at 3:03 pm
    Permalink

    im wondering the exact same thing too (specifying a group), any ideas?

    [Reply]

    Ant

    Ant Reply:

    As it turns out hibernate has a grouping solution built in that handles group validations:

    http://docs.jboss.org/hibernate/validator/4.0.1/reference/en/html/validator-usingvalidator.html#validator-usingvalidator-validationgroups

    Spring 3 however does not support grouping yet and on the block for getting fixed, hopefully soon:

    http://jira.springframework.org/browse/SPR-6373

    [Reply]

  7. Gary Written by Gary
    on May 26, 2010 at 11:33 pm
    Permalink

    I’m new to Spring MVC. I have a question. Suppose user entered “3.3xy” for price. Obviously, there’s no way the price field will be populated by a valid value. Upon returning back to the user, how can the jsp page show “3.3xy” using (form:input path=”price”)? Or will user input on price field be lost?

    [Reply]

    Ant

    Ant Reply:

    Hi Gary,
    I didn’t see anything in the hibernate documentation that specifically mentioned echoing the data back to the input form. In the Hibernate Validator section 3.1.3 though, we find out we can write our own custom messages, and there might be a way to echo the value if we could only get a handle on the input value if its supported. The validation required value is marked as {value}. Maybe it supports {input}? Otherwise I’m afraid we’ll have to write out own message interpolator.

    [Reply]

  8. jl Written by jl
    on July 1, 2010 at 6:42 am
    Permalink

    When I try to write the code as described in part “pay special attention to” I only get an error message claiming that anotation type is not applicable to this kind of declaration. Can anyone assist me with this?

    [Reply]

    Ant

    Ant Reply:

    Sounds like you’re using the hibernate @Valid annotation, make sure you are using the javax.validation.Valid as your import and not the hibernate @valid annotation. They’re slightly different and do not mean the same thing.

    [Reply]

  9. elle Written by elle
    on July 7, 2010 at 7:47 am
    Permalink

    Great post!

    But how about depended validation in the same object class, say like ZipCode can’t be null if Country is given, otherwise it is not mandatory to give Country? This would be Address class.

    [Reply]

    Ant

    Ant Reply:

    Hibernate provides group validations which is meant to serve the same purpose, but spring 3 does not support it – it would require a custom annotation to bridge the implementations.

    There is an open ticked in spring 3 – http://jira.springframework.org/browse/SPR-6373 for this very problem. Otherwise, you are free to use @Group that Hibernate validator implements:

    http://docs.jboss.org/hibernate/validator/4.0.1/reference/en/html/validator-usingvalidator.html#validator-usingvalidator-validationgroups

    Hope this helps.

    [Reply]

  10. jaanlai Written by jaanlai
    on July 14, 2010 at 2:57 am
    Permalink

    Here is my problem also brought up in Spring Forum:

    am using my own tag to go trought all the error messages like this:

    However, this is not working right at all with my javax.validation anotations in my object class. I am only getting the constraint as error message, like with @NotEmpty the message shown is NotEmpty and so on.

    I have tried to put the message on object class like:
    @NotEmpty (message=”You got an error here”)

    and codes:
    @NotEmpty (message=”{error.code}”)

    with defining the codes and translations in ValidatorMessages/messages.properties.

    I have also tried forming the code like:

    NotEmpty.myClass.MyProperty

    but the result is always the same. To me it seems like the error code is different with javax. Can you help me with this please?

    [Reply]

    Ant

    Ant Reply:

    Theres a section in the hibernate validator documentation that goes over custom messages and how to write them:

    http://docs.jboss.org/hibernate/validator/4.0.1/reference/en/html/validator-usingvalidator.html#section-message-interpolation

    If you correctly placed your custom messages at the base of your classpath, you’ll want to make sure that properties file is written correctly – take a look at the bundled resources used by default as a guide, here are some examples:

    org.hibernate.validator.constraints.NotEmpty.message=may not be empty
    org.hibernate.validator.constraints.Range.message=must be between {min} and {max}
    javax.validation.constraints.Pattern.message=must match “{regexp}”

    Notice the different message bundle property names. You’ll want to stick to how they named their message references if you want to preserve the correct message links to display properly. You can find all the resource bundled properties in the hibernate validator jar file:

    hibernate-validator-4.0.2.GA.jar?org\hibernate\validator\ValidationMessages.properties
    hibernate-validator-4.0.2.GA.jar?org\hibernate\validator\ValidationMessages_de.properties
    hibernate-validator-4.0.2.GA.jar?org\hibernate\validator\ValidationMessages_fr.properties

    Parsing curly brackets used in the annotated validations like “@NotEmpty (message=”{error.code}”)” would require a re-implementaion of the spring/hibernate integration code that would add functionality to try and map messages dynamically using the notation you attempted. You might want to look at implementing your own message interpolater (http://docs.jboss.org/hibernate/validator/4.0.1/reference/en/html/validator-bootstrapping.html) which should be able to plug into the spring framework and might cover what you’re trying to do.

    Hope this helps

    – a

    [Reply]

  11. Julien Written by Julien
    on July 20, 2010 at 4:41 pm
    Permalink

    I had this : to my applicationContext.xml, but my BindingResult always has 0 error. I don’t know understand…

    How should be configure the spring context?

    [Reply]

    Ant

    Ant Reply:

    I’m not sure I understand what you mean by the BindingResult always has 0. The spring mvc configuration should be set up as a normal spring application, it’s no different. The only difference is adding the hibernate validator jars to the classpath and annotating the validations, and of course annotating @Valid for your validation. Can you explain a little more what the trouble is?

    - a

    [Reply]

    Julien

    Julien Reply:

    Sorry, I didn’t explain well my problem. First I’m in a portlet environment. For detecting hibernate validator, I wrote this in my context :

    And for my method I’m doing this :

    protected void registration(@Valid Patient patient,
    BindingResult result) {
    if (result.hasErrors()) {
    return;
    }
    }

    But my result never has errors, even if my form is empty. I think that I missed something.

    Ant

    Ant Reply:

    Can you post your class definition for Patient? It sounds like whatever validation annotations you’re using are not being parsed in or loaded up. Try doing a single annotation first and step through your code to make sure the BindingResult result variable has errors – if it does then your jsp isn’t displaying correctly (try adding @ModelAttribute(“patient”) before @Valid to provide scope ) and if it doesn’t then double check your annotations to make sure they’re set up correctly.

  12. Stuart Gunter Written by Stuart Gunter
    on July 21, 2010 at 8:23 am
    Permalink

    As a response to the comments about group validation. I’m not sure if anyone has mentioned it already, but Spring Web Flow does handle subset validation for scenarios where you want to validate parts of the object at various stages (e.g. in a wizard).

    Have a look here for more details: http://static.springsource.org/spring-webflow/docs/2.1.x/reference/html/ch05s10.html

    [Reply]

    Ant

    Ant Reply:

    Thanks for the heads up!

    [Reply]

  13. Stefan Haberl Written by Stefan Haberl
    on July 23, 2010 at 7:55 am
    Permalink

    Hi,

    For those of you who want to integrate Hibernate Validator with Spring and want to generate a custom message:

    A workaround I’m quite happy with is to wire your Spring MessageSource into your custom Validator like so:

    @Autowired
    private MessageSource messageSource;

    and then manually resolve the error message:

    Locale locale = LocaleContextHolder.getLocale();
    String message = messageSource.getMessage(“key”, new Object[] { “Test” },
    locale);

    context.disableDefaultConstraintViolation();

    context.buildConstraintViolationWithTemplate(message).addConstraintViolation();

    HTH

    [Reply]

    Ant

    Ant Reply:

    Thanks for the workaround, I’ll point to your workaround in the article.

    [Reply]

Subscribe to comments via RSS

Leave a Reply