The Bean Validation API in Spring Roo Framework

SHARE & COMMENT :

This article is based on Spring Roo in Action, to be published Summer-2011. It is being reproduced here by permission from Manning Publications. Manning publishes MEAP (Manning Early Access Program,) eBooks and pBooks. MEAPs are sold exclusively through Manning.com. All pBook purchases include free PDF, mobi and epub. When mobile formats become available all customers will be contacted and upgraded. Visit Manning.com for more information. [ Use promotional code 'java40beat' and get 40% discount on eBooks and pBooks ]

also read:

The Bean Validation API

Introduction

Validation is a difficult topic for any application architecture. You may ask yourself a bevy of questions, such as:

  • Where do I perform validation, at the web layer, in the middle tier, or in my database?
  • How do I validate? Should I use a validation rules engine, scripted code, data-driven rules, or annotations?
  • How will my errors be returned? Should I localize the messages?

There are many APIs available to implement validation rules. Spring MVC has its own validation API, but it is MVC based and doesn’t necessarily suit the embedding rules within the entities. We want to do this because it helps us to encapsulate the behavior of validation within the entity tier. A more object-driven approach is needed. Enter the Bean Validation API.

The Bean Validation API is a recent standard. It was created by the Java EE Expert Group to address the lack of a standard validation API on the Java EE platform. This API uses Java annotations to define specific rules, which are attached to the attributes of a Java Bean. Some validations are built into the framework, such as @NotNull, @Null, @Min, @Max, @Past, @Future, @Pattern, and @Size. You can also define your own classes for validation purposes and register them using @AssertTrue or @AssertFalse.

Let’s get started with the Bean Validation API by building a simple example. We’ll define a Java Bean annotated with validations, and then we will use JUnit unit tests to exercise the Bean Validation API and generate validation errors. Later, we’ll show you how to use Bean Validation from Roo, and how to customize validation messages and build your own constraints. But, first, let’s set up our project.

Generating our project

Let’s jump on and take the Roo Shell for a test drive by generating our project. The first step is to create a directory for our project:

$ mkdir tracker

Go into the directory—this is where Roo will generate the project directories and artifacts:

$ cd tracker

Start Roo:

$ roo

Notice Roo’s shell prompt is simply:

roo>

This prompt will change based on the last command issued. Let’s keep an eye on the Roo prompt as we move forward. Now let’s start creating our project by typing pr and pressing the [TAB] key twice:

roo> proj [TAB] [TAB]

When we use the [TAB] key, Roo will automatically select the matching command with the first required option project –topLevelPackage, requiring us to only then type the project’s top level package. When we press the tab key, Roo will display the list of commands based on the starting characters typed. If there is more than one command, we will be given a list of matching commands. In this case, we get project because it is the only command starting with pr. Roo then prompts us with any required elements. For our project, the only required element is the top level package. Let’s choose a package name of org.distracted.tracker.

roo> project --topLevelPackage org.distracted.tracker 

The last element in the package defines the name of the application by default—tracker. This is due to the package and not the physical directory name. It also configures:

  • The application’s location using the default URL of http://localhost:8080/tracker
  • The project name display in SpringSource Tool Suite as tracker.
  • Maven Group Id: org.distracted.tracker.
  • Maven Artifact Id: tracker.
  • Maven Project Name: tracker.
Created /code/roo/tracker/pom.xml
	Created SRC_MAIN_JAVA
	Created SRC_MAIN_RESOURCES
	Created SRC_TEST_JAVA
	Created SRC_TEST_RESOURCES
	Created SRC_MAIN_WEBAPP
	Created SRC_MAIN_RESOURCES/META-INF/spring
	Created SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml
	Created SRC_MAIN_RESOURCES/log4j.properties
	org.distracted.tracker roo>

Notice the capitalized path elements that Roo has created? These are constants that are used throughout Roo commands. Now, let’s set up our database.

Setting up the database

To make this project easy to deal with, we will assume you don’t have a database installed on your machine. No big deal, there are a number of embeddable and standalone databases you can use. In our case, let’s use the Hypersonic SQL standalone database. Here is the persistence configuration setup command to run from the Roo shell.

roo> persistence setup --database HYPERSONIC_PERSISTENT --provider HIBERNATE

SAVE SOME TYPING

Remember, in the Shell, you can type the first two or three characters of this command—for example, per [TAB]—and Roo will complete the command for you. This goes for your options and values as well. You can type — [TAB] to see what options are available, and when an option is selected such as database, you can hit [TAB] to get the available options.

Now that we’ve created the project and installed Hibernate and JSR-303 Validations, we can move onto our discussion of the Bean Validation API.

Defining a validated bean

Using the Roo shell, we will create a ValidationExercisor class, which will allow us to experiment with the Bean Validation API:

roo> class --class ~.validations.ValidationExercisor

This command just creates a class in the org.distracted.tracker.validations package in src/main/java. In listing 1 below, we edit this class and define a simple JavaBean with some attributes that hold validation annotations. We define the class with the @RooJavaBean annotation so that the Roo shell will automatically generate getters and setters for us. We also define three fields—a String, a Date, and a BigDecimal, and we define several validation annotations for each one.

Listing 1 ValidationExercisor.java

package org.distracted.tracker.validations;
import java.math.BigDecimal;
import java.util.Date;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Future;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Length;
import org.springframework.roo.addon.javabean.RooJavaBean;
@RooJavaBean #1
public class ValidationExercisor {
	@NotNull #2
	@Length(min = 1, max = 10)
	private String myString;
	@DecimalMin("0.0") #3
	@DecimalMax("9999999.9")
	private BigDecimal myNumber;
	@Future #4
	private Date dueDate;
}

(#1) Generates getter and setter methods for this class in a file named ValidationExcercisor_Roo_JavaBean.aj
(#2) The myString attribute cannot be null and must be between 1 and 10 characters in length.
(#3) The myNumber attribute may be null but, if specified, will range between 0 and 9999999.9.
(#4) This date, if specified, must be in the future from the time when the validation is performed.

Although this isn’t an entity, we wanted to start out our discussion of Bean Validation by showing you that you can use it pretty much wherever you like it.

Roo entities

Roo Entities are JPA-annotated classes that map data to a database and specify data-type constraints and validation rules using annotations such as @NotNull, @Past, and @Size.

Spring supports Bean Validation annotations in a number of places, such as in web form validation, entity validation, and in plain JavaBeans.

Roo annotations—not just for generated code

If you create classes below your top-level package, the Roo shell will automatically look at them each time they are saved and generate any AspectJ ITDs required to add the features that you want. That’s why we can use the annotation @RooJavaBean above and can easily add features such as @RooToString to automatically generate a toString() method. Look for add-ons to be developed that take advantage of this feature, adding support for a wide variety of features without requiring you to write code.

Later on, we’ll see how we can customize validations by adding our own messages and validators but, for now, let’s get started and test our validations.

This article is based on Spring Roo in Action, to be published Summer-2011. It is being reproduced here by permission from Manning Publications. Manning publishes MEAP (Manning Early Access Program,) eBooks and pBooks. MEAPs are sold exclusively through Manning.com. All pBook purchases include free PDF, mobi and epub. When mobile formats become available all customers will be contacted and upgraded. Visit Manning.com for more information. [ Use promotional code 'java40beat' and get 40% discount on eBooks and pBooks ]

Testing the Validation Framework

Validation is performed using an implementation of the Bean Validation Framework specification (JSR-303). Spring Roo comes configured with the Hibernate Validator, which also happens to be the reference implementation of the Bean Validation Framework. Although Spring Roo automatically executes Bean Validation for us, we wanted to show the mechanisms behind Bean Validation so that you can understand what happens behind the scenes.

Let’s build a JUnit test to exercise those methods. Listing 2 is a completed JUnit 4 unit test class that interacts with the Bean Validation API.

NOTE:This test has to be created manually via the IDE in src/test/java/org/distracted/tracker/validations because the current Roo framework supports generation of test entity-based unit (mock) and integration tests only. Using the class command will put the test in the wrong parent directory, src/main/java rather than the src/test/java directory, where the maven build can recognize the testing annotations.

Listing 2 Testing the validator

package org.distracted.tracker.validations;
import java.util.Date;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.groups.Default;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.springframework.mock.staticmock.MockStaticEntityMethods;
@RunWith(JUnit4.class)
@MockStaticEntityMethods
public class ValidationExercisorTest {
	@Test
	public void testMethod() {
		ValidatorFactory defaultValidatorFactory =
		Validation.buildDefaultValidatorFactory(); #1
		Validator validator = defaultValidatorFactory.getValidator();
		ValidationExercisor exercisor = new ValidationExercisor();
		exercisor.setDueDate(new Date(System.currentTimeMillis() - 1000));
		Set<ConstraintViolation<ValidationExercisor>> constraints =
		validator.validate(exercisor, Default.class); #2
		for (ConstraintViolation<ValidationExercisor> constraint : constraints) {
			System.err.println(constraint.getMessage()); #3
		}
	}
}

#1 Configure the factory
#2 Invoke the validator
#3 Process violations

Of course, a true JUnit test would verify that the errors were triggered appropriately, but this test simply illustrates how the Validation Framework processes and delivers violations. When executed as a Unit Test in SpringSource Tool Suite or via the operating system command line mvn test, the validation constraint messages are returned in the standard error output for our two validation errors:

must be in the future
	may not be null

Since setting up and calling the validator by hand is a bit tedious, let’s see how we can get Spring Roo to wire it up for us and make our lives easier by executing validations automatically against our entities.

Using bean validation in Roo entities

Spring Roo supports automatic validation of Roo Entities, if annotated with Bean Validation annotations. The Roo entities are automatically validated when a persist or merge method call is executed. Any errors will result in the throwing of a ConstraintViolationException, which contains all ConstraintViolation instances for errors encountered during the validation process.

Turning our ValidationExercisor into a JPA Entity is simple—you just need to add three annotations, @Entity, @RooEntity, and @RooToString to the class (the @Entity annotation is from javax.persistence). Editing the ValidationExcercisor class from listing 1, we simply add the annotations to the top of the class definition:

@Entity
@RooEntity
@RooToString
@RooJavaBean
public class ValidationExercisor {
...
}

Fire up the Roo shell and watch it reconfigure your class. In general, when working on Roo-managed classes, you should leave the shell running so that it can automatically reconfigure your class, especially if you are working on your project using the SpringSource Tool Suite. When Roo is done making changes to the classes, type quit to exit the shell.

The ValidationExercisor is now an Entity, which means it will create and manage a table, validation_exercisor, containing three database fields—my_string, my_number, and due_date. Although this is an academic exercise, it shows how trivial it can be to pull a Java Bean into the JPA platform and start managing it as a JPA entity.

Let’s create a Roo-generated Integration Test for our entity and modify it to perform our validation. The command to set up our integration test is:

roo> test integration --entity ~.validations.ValidationExercisor

Our Roo-friendly integration test is shown here in listing 3, modified adding the testValidatorAutomatically() method to trigger and verify bean validations.

Listing 3 Roo integration validation test

package org.distracted.tracker.validations;
import java.util.Date;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.junit.Test;
import org.springframework.roo.addon.test.RooIntegrationTest;
@RooIntegrationTest(entity = ValidationExercisor.class,
persist = false) #1
public class ValidationExercisorIntegrationTest {
	@Test
	public void testValidatorAutomatically() {
		ValidationExercisor exercisor = new ValidationExercisor();
		exercisor.setDueDate(new Date(System.currentTimeMillis() - 1000));
		try {
		exercisor.persist(); #2
		} catch (ConstraintViolationException cve) {
			Set<ConstraintViolation<?>> constraintViolations =
			cve.getConstraintViolations();
			for (ConstraintViolation<?> violation: constraintViolations) {
				System.err.println(violation.getMessage());
			}
		}
	}
}

(#1) Disable the built-in test of the persist method.
(#2) In Roo, calling persist() automatically fires validation as part of pre-persistence processing.

For each error, an individual instance ConstraintViolation is returned. The ConstraintViolation object contains the attributes defined in table 1.

You can gain access to these fields when catching the ConstraintViolationException. Here is a more detailed code fragment with a deeper interrogation:

try {
	author.persist();
} catch (ConstraintViolationException e) {
	Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
	for (ConstraintViolation<?> cv : constraintViolations) {
	System.err.println("Invalid Value : " + cv.getInvalidValue());
	System.err.println("Property path: " + cv.getPropertyPath());
	System.err.println("Message " + cv.getMessage());
	System.err.println("Message Template: " + cv.getMessageTemplate());
	System.err.println("Root Bean" + cv.getRootBean());
	}
}

Not only can Roo manage JPA Entities using the Bean Validation Framework but it can generate automatic error handling for form elements, populating the page with messages when errors occur. Further, Roo generates client-side validations based on the built-in annotations, which will appear whenever a user attempts to enter an invalid value.

Let’s see how we can use Validation in our current application, the Task Manager. We’ll assign validation rules to the Task entity.

Validating our Task entity

It is easy to add validations to our Task object. We merely have to add the annotations to the fields we wish to validate. The modified class is shown, with Bean Validation annotations, in listing 4.

Listing 4 Task with validations

package org.distracted.tracker.model;
import java.util.Date;
import java.util.Set;
...
@Entity
@RooJavaBean
@RooToString
@RooEntity
public class Task {
	...
	@NotNull
	@Temporal(TemporalType.TIMESTAMP)
	@DateTimeFormat(style = "S-")
	private Date dateCreated;
	@Temporal(TemporalType.TIMESTAMP)
	@DateTimeFormat(style = "S-")
	private Date dateDue;
	@Temporal(TemporalType.TIMESTAMP)
	@DateTimeFormat(style = "S-")
	private Date dateComplete;
	@NotNull #1
	@Size(max = 1000) #2
	private String description;
	@Min(1) #3
	@Max(5)
	private int priority;
	private Boolean completed;
	...
}

#1 Cannot be null on persist or merge operations.
#2 The description field cannot be longer than 1,000 characters long.
#3 The priority field can range between 1 and 5.

Now that we’ve seen how we can assign validation rules to our entities, we should review the validation annotations built in to the Bean Validation Framework.

Built-in validations

The validations in table 2 are built into the Bean Validation API and can be used by any Java Object.

Some of these validations may not make sense on the surface—why would we want to define a @Null validation if it makes the field unsettable? That’s because, in the specification, the Bean Validation framework supports the concept of validation groups. In the current release of Roo, the only validation group supported is called Default, so, unless Roo Entities begin to support validations with multiple groups, this particular validation won’t really be easily used.

So far, we’ve looked at implementing validations and we’ve seen how Spring Roo automatically executes validation checks before saving an entity. Now let’s take a look at how to customize the error messages provided by the annotation.

Custom validation messages

The Bean Validation API is an official Java JSR (JSR-303). The specification states that messages can be customized and localized. Let’s take a look at the customization process. The Bean Validation Framework provides for customization of messages, either in the annotations themselves or in externalized property files.

To define your own validation messages within the annotations themselves, use the message attribute:

@NotNull(message = "last name is required.")
	private String lastName;

You can include the value of any attribute supplied to the annotation. Substituting parameters that are set by the annotation is easy. Just wrap them with braces as in this numeric range validation example:

@Length(min = 1, max = 10,
	message = "The value must be between {min} and {max}")
	private String myString;
	@Min(value = 0, message="must be greater than or equal to {value}")
	@Max(value = 5, message="must be less than or equal to {value}")
	private int priority;

You can use any attribute defined in the annotation itself. Currently, the Bean Validation Framework does not supply the value of the object itself to the validation error.

VALIDATION WITH SINGLE-VALUE ANNOTATIONS

You can see in the examples above that the @Min and @Max annotations take a single value, specified as the parameter named value. An annotation with a single required parameter can be specified without the parameter by name, such as in @Max(5), so you need to be explicit with parameter names when using more than one parameter.

But you don’t want to put messages inside of your code. As with Spring’s property placeholders, the Bean Validation Framework supports externalizing messages to localizable property files.

This article is based on Spring Roo in Action, to be published Summer-2011. It is being reproduced here by permission from Manning Publications. Manning publishes MEAP (Manning Early Access Program,) eBooks and pBooks. MEAPs are sold exclusively through Manning.com. All pBook purchases include free PDF, mobi and epub. When mobile formats become available all customers will be contacted and upgraded. Visit Manning.com for more information. [ Use promotional code 'java40beat' and get 40% discount on eBooks and pBooks ]

Externalizing message configuration

The default message definitions file for implementing custom messages for the Bean Validation Framework, as specified in the JSR, is /ValidationMessages.properties. Let’s create this file in the root of src/main/resources, so we can validate using customized messages. A sample follows.

nullValidationMessage=cannot be null.
	minValidationMessage=must be greater than or equal to {value}
	maxValidationMessage=must be less than or equal to {value}

The updated annotations for the ranking field can reference these names using braces:

@NotNull(message = "{nullValidationMessage}")
	@Min(value = 0, message="{minValidationMessage}")
	@Max(value = 5, message="{maxValidationMessage}")
	private int ranking;

Replacing the default message for a given annotation is also easy. First, using SpringSource Tool Suite, CTRL/CMD-Click on the @Max annotation to view the source code. You will see a line that looks like this:

String message() default "{javax.validation.constraints.Min.message}";

Your customized message can replace the default, built-in message by defining a replacement message for the javax.validation.constraints.Min.message field.

// the annotated field in a JPA entity:
@Max(value = 5)
private int ranking;
// in ValidationMessages.properties
javax.validation.constraints.Min.message=can be no larger than {value}

What about localizing our messages? That is also simple. Since Roo uses Java resource bundles, we merely have to supply localized versions of our ValidationMessages.properties file, such as ValidationMessages_de_DE.properties.

TESTING LOCALES

Testing your Locales can be an interesting challenge. On the Mac, for example, you can use the System Preferences Languages pane to drag/drop your target language to the top. More information can be found at http://java.sun.com/developer/technicalArticles/J2SE/locale/#using.

So far, we’ve seen how to annotate our entities with validation constraints and how to customize the validation messages, including the default messages for validation.

Custom validation

So, what about writing our own validation annotations? In some cases, the built-in validation annotations will not be enough for our needs. As an example, consider something as complex as ISBN validation. To make this a bit simpler, let’s assume we are using the pre-2007 10-digit ISBN because the 13-digit one is more complex. The 10-digit ISBN checksum is validated by summing each of the first nine numbers in sequence and multiplying it by 10 minus the current position. The sum is divided by 11, and the modulus is taken. The modulus is subtracted from 11, and the result is the checksum.

To build this custom validation, you first have to define a custom validation annotation. In the ISBNCheckDigit annotation in listing 5, we’ll define our validator as a no-argument validation annotation and add the @Pattern and @NotNull annotations, which will require the ISBN is always supplied and exactly 10 digits long.

Listing 5 ISBNCheckDigit validation annotation

package com.chariot.jpademo.model.validation;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
// these are required, and help document the validation to the
// validation engine. Note the validatedBy entry, which ties the
// annotation to a validator
@Documented
@Constraint(validatedBy = ISBNWithCheckDigitValidator.class)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Pattern(regexp = "[0-9]{10}") #1
@NotNull
public @interface ISBNWithCheckDigit {
	String message() default "{message-isbn-valid}"; #2
	Class<?>[] groups() default { }; #3
	Class<? extends Payload>[] payload() default {}; #3
}

#1 Add Regular Expression-based pattern validation—10 digits
#2 Externalize message
#3 These are required

The groups() and payload()annotations are required, as are the @Documented, @Target, and @Retention annotations. The Constraint annotation defines what Java Bean will perform our validation for us. The validator class must implement a templated ConstraintValidator interface, which requires us to implement the initialize and isValid methods, as shown in listing 6.

Listing 6 The ISBNCheckDigitValidator

package com.chariot.jpademo.model.validation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.constraints.AssertTrue;
import org.apache.log4j.Logger;
public class ISBNWithCheckDigitValidator implements
ConstraintValidator<ISBNWithCheckDigit, String> {
	private static Logger log = Logger
	.getLogger(ISBNWithCheckDigitValidator.class);
	public void initialize(ISBNWithCheckDigit constraintAnnotation) { #1
}

@AssertTrue #2
public boolean isValid(String isbn, ConstraintValidatorContext context) {
	log.info("ISBN is " + isbn + " with length of " + isbn.length());
	int checkSumAccum = 0;
	int factor = 10;
	int actualCheckSum = Integer.parseInt(Character
	.toString(isbn.charAt(9)));
	log.info("actual checksum = " + actualCheckSum);
	for (int i = 0; i < isbn.length() - 1; i++) {
		int val = Integer.parseInt(Character.toString(isbn.charAt(i)));
		log.info("value for position " + i + " is " + val + ", factor = "
		+ factor + ", result = " + factor * val);
		checkSumAccum += factor * val;
		factor--;
		log.info("accumulator is now " + checkSumAccum);
	}
	int modulus = checkSumAccum % 11;
	int checkDigit = 11 - modulus;
	log.info("Checksum accum is now modded by 11, and is: " + modulus);
	log.info("The checksum digit is 11 - modulus, which is " + checkDigit);
	return checkDigit == actualCheckSum;
	}
}

#1 This method is a required method from the ContraintValidator interface
#2 Perform validation
#3 Accumulate sum of check values
#4 Perform the modulus and subtract

Yes, that was a complex example! In the real world, validation may take place using external resources such as a database or other Spring Beans. Technically, since Roo entities are marked as @Configurable by the @RooEntity annotation, we can also include references to other Spring Beans using the @Autowired annotation. Please be aware of the overhead that the calls to outside beans may incur and don’t try to dig into child collections because they may end up resulting in additional fetches or may throw exceptions.

To wrap up our discussion on validations, let’s look at writing a rule using arbitrary code within our entities. These rules can be defined using the @AssertTrue or @AssertFalse validations.

Using the @AssertTrue validation

Take a very arbitrary example of a popularity-ranking engine. Let’s say you want to delegate your Author popularity check to a Spring Bean, which can be implemented in a number of ways. In one implementation, the author needs to be interrogated, for some strange reason, to make sure anyone with the last name of Rimple or Dickens automatically has a high popularity rating. Pretentious, moi?

In this case, we are going to use another annotation, @AssertTrue, which lets us code the validation inside of our entity object. This validation rule expects to annotate a method named isValid(). During validation, the results of the validation determine whether this validation rule is fired. See listing 7.

Listing 7 Validation within an Entity using @AssertTrue

...
@Entity
@RooJavaBean
@RooEntity
public class Author {
	...
	@AssertTrue(message="Must be a 5 if it's the guys from Roo in Action!")
	@Transient
	private boolean isValid() {
		if (lastName == null) return true;
		if (lastName.startsWith("Dickens") ||
		lastName.startsWith("Rimple")) {
			return ranking == 5;
		} else {
			return true;
		}
	}
	...
}

(#1) Establish the validation with the @AssertTrue annotation. Provide the message for the error, if this method returns false.
(#2) The method that implements @AssertTrue has to have the signature private boolean isValid().

In this example, our isValid() method is annotated with @AssertTrue, using a custom message. Since our method is executed on the instance of the bean itself, it has access to other fields within our entity during the validation process.

Let’s look at an integration test fragment that exercises this logic. Review listing 8 below.

Listing 8 Testing the @AssertTrue validator

@Test
public void testRooInActionAuthorsWithError() {
	AuthorDataOnDemand dod = new AuthorDataOnDemand();
	Author a = dod.getNewTransientAuthor(0);
	// should pass
	a.setRanking(3);
	a.setLastName("Jones");
	a.persist();
	Assert.assertNotNull(a.getId());
	// should throw our exception
	a = dod.getNewTransientAuthor(2);
	a.setRanking(1);
	a.setLastName("Rimple");
	try {
		a.persist();
	} catch (ConstraintViolationException e) {
		return;
	}
	Assert.fail("We should have fired a validation exception.");
}

This example uses the class AuthorDataOnDemand that gets generated using the –testAutomatically flag when we created the entity. Don’t worry if you forget to do this, you can always generate a data-on-demand class using the dod Roo shell command, or get one built for you when you build an integration test using the test integration command.

This mechanism is much easier to deal with than the alternative mechanism for rule-based or multifield-based validation, which is by building validation annotations at the class level. For more details on the Bean Validation API, visit the documentation for JSR-303 online.

IF YOUR UNIT TESTS FAIL AFTER CONFIGURING CUSTOM VALIDATIONS…

If you have a problem with your entity integration tests once you code custom validations, you’re not alone. While this may be corrected in a future release, the JSR-303 validations aren’t completely supported by Roo, and your custom validations will never be. One way to handle this is to open up the DataOnDemand class in your integration test package and implement the getNewTransientEntityName method, replacing it with one that returns valid values for your bean. Spring Roo will remove the method definition from the AspectJ ITD file automatically.

So, if you need to validate your beans before persisting them, you can use the Bean Validation Framework. Try to stick to a few simple rules:

  • Validation by Composition—When building validation for a particular bean, go ahead and stack validators on a field. If you’d like to compose your own grouped validation, just build a validation annotation that is comprised of the others you need. You can get a lot done by using a combination of @NotNull and @Pattern, for example.
  • Be sparing in your processing power—Just because you can call a stored procedure behind a service to validate an entry in that list, do you really want to? Realize that if you’re saving a collection of objects, this validation will be called on each item within the list, thus causing many calls to the same procedure.
  • Use @AssertTrue and isValid() for multicolumn checks—A quick way to get your complex, logic-based validation to work is to build an isValid() method within your entity, annotating it with @AssertTrue. Within this method, you have access to other fields in the entity.

also read:

Summary

In this article, we’ve discussed the Bean Validation Framework API and how Spring Roo uses it to validate data. We showed you how to use Bean Validation from Roo and how to customize validation messages and build your own constraints.

Comments

comments

About Krishna Srinivasan

He is Founder and Chief Editor of JavaBeat. He has more than 8+ years of experience on developing Web applications. He writes about Spring, DOJO, JSF, Hibernate and many other emerging technologies in this blog.

Speak Your Mind

*

Close
Please support the site
By clicking any of these buttons you help our site to get better