Hibernate Validator Reference PDF
Hibernate Validator Reference PDF
Final - JSR
349 Reference Implementation
Reference Guide
Hardy Ferentschik, Gunnar Morling
2017-10-19
Table of Contents
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1. Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1. Project set up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2. Applying constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3. Validating constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.4. Java 8 support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.5. Where to go next? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2. Declaring and validating bean constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.1. Declaring bean constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2. Validating bean constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.3. Built-in constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3. Declaring and validating method constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.1. Declaring method constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.2. Validating method constraints. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
3.3. Built-in method constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4. Interpolating constraint error messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.1. Default message interpolation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.2. Custom message interpolation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
5. Grouping constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
5.1. Requesting groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
5.2. Group inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
5.3. Defining group sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
5.4. Redefining the default group sequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
5.5. Group conversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
6. Creating custom constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
6.1. Creating a simple constraint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
6.2. Class-level constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
6.3. Cross-parameter constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
6.4. Constraint composition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
7. Configuring via XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
7.1. Configuring the validator factory in validation.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
7.2. Mapping constraints via constraint-mappings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
8. Bootstrapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
8.1. Retrieving ValidatorFactory and Validator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
8.2. Configuring a ValidatorFactory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
8.3. Configuring a Validator. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
9. Using constraint metadata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
9.1. BeanDescriptor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
9.2. PropertyDescriptor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
9.3. MethodDescriptor and ConstructorDescriptor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
9.4. ElementDescriptor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
9.5. GroupConversionDescriptor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
9.6. ConstraintDescriptor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
10. Integrating with other frameworks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
10.1. ORM integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
10.2. JSF & Seam. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
10.3. CDI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
10.4. Java EE. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
10.5. JavaFX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
11. Hibernate Validator Specifics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
11.1. Public API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
11.2. Fail fast mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
11.3. Relaxation of requirements for method validation in class hierarchies . . . . . . . . . . . . . . . 136
11.4. Programmatic constraint definition and declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
11.5. Applying programmatic constraint declarations to the default validator factory . . . . . . 141
11.6. Advanced constraint composition features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
11.7. Extensions of the Path API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
11.8. Dynamic payload as part of ConstraintViolation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
11.9. ParameterMessageInterpolator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
11.10. ResourceBundleLocator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
11.11. Custom contexts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
11.12. ParaNamer based ParameterNameProvider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
11.13. Unwrapping values. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
11.14. Providing constraint definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
11.15. Customizing class-loading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
11.16. Time providers for @Future and @Past . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
12. Annotation Processor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
12.1. Prerequisites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
12.2. Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
12.3. Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
12.4. Using the Annotation Processor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
12.5. Known issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
13. Further reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
Preface
Validating data is a common task that occurs throughout all application layers, from the
presentation to the persistence layer. Often the same validation logic is implemented in each
layer which is time consuming and error-prone. To avoid duplication of these validations,
developers often bundle validation logic directly into the domain model, cluttering domain
classes with validation code which is really metadata about the class itself.
JSR 349 - Bean Validation 1.1 - defines a metadata model and API for entity and method
validation. The default metadata source are annotations, with the ability to override and extend
the meta-data through the use of XML. The API is not tied to a specific application tier nor
programming model. It is specifically not tied to either web or persistence tier, and is available
for both server-side application programming, as well as rich client Swing application
developers.
Hibernate Validator is the reference implementation of this JSR 349. The implementation itself
1
as well as the Bean Validation API and TCK are all provided and distributed under the Apache
Software License 2.0.
2
Chapter 1. Getting started
This chapter will show you how to get started with Hibernate Validator, the reference
implementation (RI) of Bean Validation. For the following quick-start you need:
• A JDK >= 6
• Apache Maven
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.2.Final</version>
</dependency>
1.1.1. Unified EL
Hibernate Validator requires an implementation of the Unified Expression Language (JSR 341)
for evaluating dynamic expressions in constraint violation messages (see Default message
interpolation). When your application runs in a Java EE container such as JBoss AS, an EL
implementation is already provided by the container. In a Java SE environment, however, you
have to add an implementation as dependency to your POM file. For instance you can add the
following two dependencies to use the JSR 341 reference implementation:
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.1-b08</version>
</dependency>
3
For environments where one cannot provide a EL implementation Hibernate
1.1.2. CDI
Bean Validation defines integration points with CDI (Contexts and Dependency Injection for
TM
Java EE, JSR 346). If your application runs in an environment which does not provide this
integration out of the box, you may use the Hibernate Validator CDI portable extension by
adding the following Maven dependency to your POM:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator-cdi</artifactId>
<version>5.4.2.Final</version>
</dependency>
Note that adding this dependency is usually not required for applications running on a Java EE
application server. You can learn more about the integration of Bean Validation and CDI in CDI.
Hibernate Validator supports running with a security manager being enabled. To do so, you must
assign several permissions to the Hibernate Validator and the Bean Validation API code bases.
The following shows how to do this via a policy file as processed by the Java default policy
implementation:
4
Example 4. Policy file for using Hibernate Validator with a security manager
permission org.hibernate.validator.HibernateValidatorPermission
"accessPrivateMembers";
All API invocations requiring special permissions are done via privileged actions. This means
only Hibernate Validator and the Bean Validation API themselves need the listed permissions.
You don’t need to assign any permissions to other code bases calling Hibernate Validator.
The WildFly application server contains Hibernate Validator out of the box. In order to update
the server modules for Bean Validation API and Hibernate Validator to the latest and greatest,
the patch mechanism of WildFly can be used.
You can download the patch file from SourceForge or from Maven Central using the following
dependency:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator-modules</artifactId>
<version>5.4.2.Final</version>
<classifier>wildfly-10.1.0.Final-patch</classifier>
<type>zip</type>
</dependency>
Having downloaded the patch file, you can apply it to WildFly by running this command:
5
Example 6. Applying the WildFly patch
In case you want to undo the patch and go back to the version of Hibernate Validator originally
coming with the server, run the following command:
You can learn more about the WildFly patching infrastructure in general here and here.
package org.hibernate.validator.referenceguide.chapter01;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@NotNull
private String manufacturer;
@NotNull
@Size(min = 2, max = 14)
private String licensePlate;
@Min(2)
private int seatCount;
6
The @NotNull, @Size and @Min annotations are used to declare the constraints which should
be applied to the fields of a Car instance:
• licensePlate must never be null and must be between 2 and 14 characters long
You can find the complete source code of all examples used in this reference
guide in the Hibernate Validator source repository on GitHub.
package org.hibernate.validator.referenceguide.chapter01;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.junit.BeforeClass;
import org.junit.Test;
@BeforeClass
public static void setUpValidator() {
ValidatorFactory factory = Validation
.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
@Test
public void manufacturerIsNull() {
Car car = new Car( null, "DD-AB-123", 4 );
Set<ConstraintViolation<Car>> constraintViolations =
validator.validate( car );
assertEquals( 1, constraintViolations.size() );
assertEquals( "may not be null", constraintViolations.iterator()
.next().getMessage() );
}
7
@Test
public void licensePlateTooShort() {
Car car = new Car( "Morris", "D", 4 );
Set<ConstraintViolation<Car>> constraintViolations =
validator.validate( car );
assertEquals( 1, constraintViolations.size() );
assertEquals(
"size must be between 2 and 14",
constraintViolations.iterator().next().getMessage()
);
}
@Test
public void seatCountTooLow() {
Car car = new Car( "Morris", "DD-AB-123", 1 );
Set<ConstraintViolation<Car>> constraintViolations =
validator.validate( car );
assertEquals( 1, constraintViolations.size() );
assertEquals(
"must be greater than or equal to 2",
constraintViolations.iterator().next().getMessage()
);
}
@Test
public void carIsValid() {
Car car = new Car( "Morris", "DD-AB-123", 2 );
Set<ConstraintViolation<Car>> constraintViolations =
validator.validate( car );
assertEquals( 0, constraintViolations.size() );
}
}
The validate() method returns a set of ConstraintViolation instances, which you can
iterate over in order to see which validation errors occurred. The first three test methods show
some expected constraint violations:
If the object validates successfully, validate() returns an empty set as you can see in
8
carIsValid().
Note that only classes from the package javax.validation are used. These are provided from
the Bean Validation API. No classes from Hibernate Validator are directly referenced, resulting
in portable code.
In Java 8 it is possible to use annotations in any location a type is used. This includes type
arguments. Hibernate Validator supports the validation of constraints defined on type
arguments of collections, maps, and custom parameterized types. The Type arguments
constraints chapter provides further information on how to apply and use type argument
constraints.
The Java 8 Reflection API can now retrieve the actual parameter names of a method or
constructor. Hibernate Validator uses this ability to report the actual parameter names instead
of arg0, arg1, etc. The ParameterNameProvider chapter explains how to use the new
reflection based parameter name provider.
Java 8 introduces a new date/time API. Hibernate Validator provides full support for the new
API where @Future and @Past constraints can be applied on the new types. The complete list
of types supported for @Future and @Past can be found in Bean Validation constraints.
Hibernate Validator provides also support for Java 8 Optional type, by unwrapping the
Optional instance and validating the internal value. Optional unwrapper provides examples
and a further discussion.
9
reading.
To learn more about the validation of beans and properties, just continue reading Declaring and
validating bean constraints. If you are interested in using Bean Validation for the validation of
method pre- and postcondition refer to Declaring and validating method constraints. In case
your application has specific validation requirements have a look at Creating custom
constraints.
10
Chapter 2. Declaring and validating bean
constraints
In this chapter you will learn how to declare (see Declaring bean constraints) and validate (see
Validating bean constraints) bean constraints. Built-in constraints provides an overview of all
built-in constraints coming with Hibernate Validator.
If you are interested in applying constraints to method parameters and return values, refer to
Declaring and validating method constraints.
• field constraints
• property constraints
• class constraints
Not all constraints can be placed on all of these levels. In fact, none of the
default constraints defined by Bean Validation can be placed at class level.
11
Example 10. Field-level constraints
package org.hibernate.validator.referenceguide.chapter02.fieldlevel;
@NotNull
private String manufacturer;
@AssertTrue
private boolean isRegistered;
When using field-level constraints field access strategy is used to access the value to be
validated. This means the validation engine directly accesses the instance variable and does not
invoke the property accessor method even if such an accessor exists.
Constraints can be applied to fields of any access type (public, private etc.). Constraints on static
fields are not supported, though.
should be used, because the byte code enhancing library won’t be able to
determine a field access via reflection.
If your model class adheres to the JavaBeans standard, it is also possible to annotate the
properties of a bean class instead of its fields. Property-level constraints uses the same entity as
in Field-level constraints, however, property level constraints are used.
12
Example 11. Property-level constraints
package org.hibernate.validator.referenceguide.chapter02.propertylevel;
@NotNull
public String getManufacturer() {
return manufacturer;
}
@AssertTrue
public boolean isRegistered() {
return isRegistered;
}
The property’s getter method has to be annotated, not its setter. That way
also read-only properties can be constrained which have no setter method.
When using property level constraints property access strategy is used to access the value to be
validated, i.e. the validation engine accesses the state via the property accessor method.
Starting from Java 8, it is possible to specify constraints directly on the type argument of a
parameterized type. However, this requires that ElementType.TYPE_USE is specified via
@Target in the constraint definition. To maintain backwards compatibility, built-in Bean
Validation as well as Hibernate Validator specific constraints do not yet specify
13
ElementType.TYPE_USE. To make use of type argument constraints, custom constraints must
be used (see Creating custom constraints).
Hibernate Validator validates type arguments constraints specified on collections, map values,
java.util.Optional, and custom parameterized types.
With Iterable
When applying constraints on an Iterable type argument, Hibernate Validator will validate
each element. Type argument constraint on List shows an example of a List with a type
argument constraint.
package
org.hibernate.validator.referenceguide.chapter02.typeargument.list;
@Valid
private List<@ValidPart String> parts = new ArrayList<>();
//...
assertEquals( 1, constraintViolations.size() );
assertEquals(
"'null' is not a valid car part.",
constraintViolations.iterator().next().getMessage()
);
assertEquals( "parts[1].<collection element>",
constraintViolations.iterator().next().getPropertyPath().
toString() );
14
With Map
Type argument constraints are also validated for map values. Constraints on the key are
ignored. Type argument constraint on maps shows an example of a Map value with a type
argument constraint.
package
org.hibernate.validator.referenceguide.chapter02.typeargument.map;
@Valid
private EnumMap<FuelConsumption, @MaxAllowedFuelConsumption Integer>
fuelConsumption = new EnumMap<>( FuelConsumption.class );
//...
assertEquals( 1, constraintViolations.size() );
assertEquals( "20 is outside the max fuel consumption.",
constraintViolations.iterator().next().getMessage() );
With java.util.Optional
When applying a constraint on the type argument of Optional, Hibernate Validator will
automatically unwrap the type and validate the internal value. Type argument constraint on
Optional shows an example of an Optional with a type argument constraint.
15
Example 14. Type argument constraint on Optional
package
org.hibernate.validator.referenceguide.chapter02.typeargument.optional;
//...
assertEquals( 1, constraintViolations.size() );
assertEquals( "Not enough towing capacity.",
constraintViolations.iterator().next().getMessage() );
assertEquals( "towingCapacity",
constraintViolations.iterator().next().getPropertyPath().
toString() );
Type arguments constraints can with two restrictions also be used with custom types. First, a
ValidatedValueUnwrapper must be registered for the custom type allowing to retrieve the
value to validate (see Unwrapping values). Second, only types with one type arguments are
supported. Parameterized types with two or more type arguments are not checked for type
argument constraints. This limitation might change in future versions.
16
package
org.hibernate.validator.referenceguide.chapter02.typeargument.custom;
//...
package
org.hibernate.validator.referenceguide.chapter02.typeargument.custom;
package
org.hibernate.validator.referenceguide.chapter02.typeargument.custom;
17
package
org.hibernate.validator.referenceguide.chapter02.typeargument.custom;
@Override
public Type getValidatedValueType(Type valueType) {
return Gear.class;
}
}
Last but not least, a constraint can also be placed on the class level. In this case not a single
property is subject of the validation but the complete object. Class-level constraints are useful if
the validation depends on a correlation between several properties of an object.
The Car class in Class-level constraint has the two attributes seatCount and passengers and
it should be ensured that the list of passengers has not more entries than seats are available.
For that purpose the @ValidPassengerCount constraint is added on the class level. The
validator of that constraint has access to the complete Car object, allowing to compare the
numbers of seats and passengers.
Refer to Class-level constraints to learn in detail how to implement this custom constraint.
18
Example 16. Class-level constraint
package org.hibernate.validator.referenceguide.chapter02.classlevel;
@ValidPassengerCount
public class Car {
//...
}
When a class implements an interface or extends another class, all constraint annotations
declared on the super-type apply in the same manner as the constraints specified on the class
itself. To make things clearer let’s have a look at the following example:
package org.hibernate.validator.referenceguide.chapter02.inheritance;
@NotNull
public String getManufacturer() {
return manufacturer;
}
//...
}
package org.hibernate.validator.referenceguide.chapter02.inheritance;
@NotNull
public String getRentalStation() {
return rentalStation;
}
//...
}
19
Here the class RentalCar is a subclass of Car and adds the property rentalStation. If an
instance of RentalCar is validated, not only the @NotNull constraint on rentalStation is
evaluated, but also the constraint on manufacturer from the parent class.
The same would be true, if Car was not a superclass but an interface implemented by
RentalCar.
The Bean Validation API does not only allow to validate single class instances but also complete
object graphs (cascaded validation). To do so, just annotate a field or property representing a
reference to another object with @Valid as demonstrated in Cascaded validation.
package org.hibernate.validator.referenceguide.chapter02.objectgraph;
@NotNull
@Valid
private Person driver;
//...
}
package org.hibernate.validator.referenceguide.chapter02.objectgraph;
@NotNull
private String name;
//...
}
If an instance of Car is validated, the referenced Person object will be validated as well, as the
driver field is annotated with @Valid. Therefore the validation of a Car will fail if the name
field of the referenced Person instance is null.
The validation of object graphs is recursive, i.e. if a reference marked for cascaded validation
points to an object which itself has properties annotated with @Valid, these references will be
followed up by the validation engine as well. The validation engine will ensure that no infinite
20
loops occur during cascaded validation, for example if two objects hold references to each other.
Note that null values are getting ignored during cascaded validation.
Object graph validation also works for collection-typed fields. That means any attributes that
• are arrays
• implement java.util.Map
can be annotated with @Valid, which will cause each contained element to be validated, when
the parent object is validated.
package
org.hibernate.validator.referenceguide.chapter02.objectgraph.list;
@NotNull
@Valid
private List<Person> passengers = new ArrayList<Person>();
//...
}
So when validating an instance of the Car class shown in Cascaded validation of a collection, a
ConstraintViolation will be created, if any of the Person objects contained in the
passengers list has a null name.
The first step towards validating an entity instance is to get hold of a Validator instance. The
road to this instance leads via the Validation class and a ValidatorFactory. The easiest
way is to use the static method Validation#buildDefaultValidatorFactory():
21
Example 20. Validation#buildDefaultValidatorFactory()
This bootstraps a validator in the default configuration. Refer to Bootstrapping to learn more
about the different bootstrapping methods and how to obtain a specifically configured
Validator instance.
The Validator interface contains three methods that can be used to either validate entire
entities or just single properties of the entity.
All three methods return a Set<ConstraintViolation>. The set is empty, if the validation
succeeds. Otherwise a ConstraintViolation instance is added for each violated constraint.
All the validation methods have a var-args parameter which can be used to specify, which
validation groups shall be considered when performing the validation. If the parameter is not
specified the default validation group (javax.validation.groups.Default) is used. The
topic of validation groups is discussed in detail in Grouping constraints.
Validator#validate()
Use the validate() method to perform validation of all constraints of a given bean. Using
Validator#validate() shows the validation of an instance of the Car class from Property-
level constraints which fails to satisfy the @NotNull constraint on the manufacturer property.
The validation call therefore returns one ConstraintViolation object.
assertEquals( 1, constraintViolations.size() );
assertEquals( "may not be null", constraintViolations.iterator().next()
.getMessage() );
Validator#validateProperty()
With help of the validateProperty() you can validate a single named property of a given
object. The property name is the JavaBeans property name.
22
Example 22. Using Validator#validateProperty()
assertEquals( 1, constraintViolations.size() );
assertEquals( "may not be null", constraintViolations.iterator().next()
.getMessage() );
Validator#validateValue()
By using the validateValue() method you can check whether a single property of a given
class can be validated successfully, if the property had the specified value:
assertEquals( 1, constraintViolations.size() );
assertEquals( "may not be null", constraintViolations.iterator().next()
.getMessage() );
Now it is time to have a closer look at what a ConstraintViolation is. Using the different
methods of ConstraintViolation a lot of useful information about the cause of the validation
failure can be determined. The following gives an overview of these methods. The values under
"Example" column refer to Using Validator#validate().
23
getMessage()
The interpolated error message
Example
"may not be null"
getMessageTemplate()
The non-interpolated error message
Example
"{… NotNull.message}"
getRootBean()
The root bean being validated
Example
car
getRootBeanClass()
The class of the root bean being validated
Example
Car.class
getLeafBean()
If a bean constraint, the bean instance the constraint is applied on; if a property constraint,
the bean instance hosting the property the constraint is applied on
Example
car
getPropertyPath()
The property path to the validated value from root bean
Example
contains one node with kind PROPERTY and name "manufacturer"
getInvalidValue()
The value failing to pass the constraint
Example
null
24
getConstraintDescriptor()
Constraint metadata reported to fail
Example
descriptor for @NotNull
Below you can find a list of all constraints specified in the Bean Validation API. All these
constraints apply to the field/property level, there are no class-level constraints defined in the
Bean Validation specification. If you are using the Hibernate object-relational mapper, some of
the constraints are taken into account when creating the DDL for your model (see "Hibernate
metadata impact").
@AssertFalse
Checks that the annotated element is false
@AssertTrue
Checks that the annotated element is true
25
@DecimalMax(value=, inclusive=)
Checks whether the annotated value is less than the specified maximum, when
inclusive=false. Otherwise whether the value is less than or equal to the specified
maximum. The parameter value is the string representation of the max value according to
the BigDecimal string representation.
@DecimalMin(value=, inclusive=)
Checks whether the annotated value is larger than the specified minimum, when
inclusive=false. Otherwise whether the value is larger than or equal to the specified
minimum. The parameter value is the string representation of the min value according to the
BigDecimal string representation.
@Digits(integer=, fraction=)
Checks whether the annotated value is a number having up to integer digits and
fraction fractional digits
@Future
Checks whether the annotated date is in the future
26
Supported data types
java.util.Date, java.util.Calendar,
java.time.chrono.ChronoZonedDateTime, java.time.Instant,
java.time.OffsetDateTime; additionally supported by HV, if the Joda Time
date/time API is on the classpath: any implementations of ReadablePartial and
ReadableInstant
@Max(value=)
Checks whether the annotated value is less than or equal to the specified maximum
@Min(value=)
Checks whether the annotated value is higher than or equal to the specified minimum
@NotNull
Checks that the annotated value is not null
27
@Null
Checks that the annotated value is null
@Past
Checks whether the annotated date is in the past
@Pattern(regex=, flags=)
Checks if the annotated string matches the regular expression regex considering the given
flag match
@Size(min=, max=)
Checks if the annotated element’s size is between min and max (inclusive)
@Valid
Performs validation recursively on the associated object. If the object is a collection or an
28
array, the elements are validated recursively. If the object is a map, the value elements are
validated recursively.
On top of the parameters listed above each constraint has the parameters
In addition to the constraints defined by the Bean Validation API Hibernate Validator provides
several useful custom constraints which are listed below. With one exception also these
constraints apply to the field/property level, only @ScriptAssert is a class-level constraint.
@CreditCardNumber(ignoreNonDigitCharacters=)
Checks that the annotated character sequence passes the Luhn checksum test. Note, this
validation aims to check for user mistakes, not credit card validity! See also Anatomy of
Credit Card Numbers. ignoreNonDigitCharacters allows to ignore non digit characters.
The default is false.
@Currency(value=)
Checks that the currency unit of the annotated javax.money.MonetaryAmount is part of
the specified currency units.
@EAN
29
Checks that the annotated character sequence is a valid EAN barcode. type determines the
type of barcode. The default is EAN-13.
@Email
Checks whether the specified character sequence is a valid email address. The optional
parameters regexp and flags allow to specify an additional regular expression (including
regular expression flags) which the email must match.
@Length(min=, max=)
Validates that the annotated character sequence is between min and max included
30
@Mod10Check(multiplier=, weight=, startIndex=, endIndex=,
checkDigitIndex=, ignoreNonDigitCharacters=)
Checks that the digits within the annotated character sequence pass the generic mod 10
checksum algorithm. multiplier determines the multiplier for odd numbers (defaults to
3), weight the weight for even numbers (defaults to 1). startIndex and endIndex allow to
only run the algorithm on the specified sub-string. checkDigitIndex allows to use an
arbitrary digit within the character sequence as the check digit. If not specified it is assumed
that the check digit is part of the specified range. Last but not least,
ignoreNonDigitCharacters allows to ignore non digit characters.
@NotBlank
Checks that the annotated character sequence is not null and the trimmed length is greater
than 0. The difference to @NotEmpty is that this constraint can only be applied on strings
and that trailing white-spaces are ignored.
@NotEmpty
Checks whether the annotated element is not null nor empty
31
Supported data types
CharSequence, Collection, Map and arrays
@Range(min=, max=)
Checks whether the annotated value lies between (inclusive) the specified minimum and
maximum
@SafeHtml(whitelistType= , additionalTags=,
additionalTagsWithAttributes=)
Checks whether the annotated value contains potentially malicious fragments such as
<script/>. In order to use this constraint, the jsoup library must be part of the class path.
With the whitelistType attribute a predefined whitelist type can be chosen which can be
refined via additionalTags or additionalTagsWithAttributes. The former allows to
add tags without any attributes, whereas the latter allows to specify tags and optionally
allowed attributes using the annotation @SafeHtml.Tag.
32
Hibernate metadata impact
None
Hibernate Validator offers also some country specific constraints, e.g. for the validation of social
security numbers.
@CNPJ
Checks that the annotated character sequence represents a Brazilian corporate tax payer
registry number (Cadastro de Pessoa Juríeddica)
Country
Brazil
@CPF
Checks that the annotated character sequence represents a Brazilian individual taxpayer
33
registry number (Cadastro de Pessoa Fídsica)
Country
Brazil
@TituloEleitoral
Checks that the annotated character sequence represents a Brazilian voter ID card number
(Título Eleitoral)
Country
Brazil
@NIP
Checks that the annotated character sequence represents a Polish VAT identification
number (NIP)
Country
Poland
@PESEL
Checks that the annotated character sequence represents a Polish national identification
number (PESEL)
34
Hibernate metadata impact
None
Country
Poland
@REGON
Checks that the annotated character sequence represents a Polish taxpayer identification
number (REGON). Can be applied to both 9 and 14 digits versions of REGON
Country
Poland
In some cases neither the Bean Validation constraints nor the custom
constraints provided by Hibernate Validator will fulfill your requirements. In
this case you can easily write your own constraint. You can find more
information in Creating custom constraints.
35
Chapter 3. Declaring and validating method
constraints
As of Bean Validation 1.1, constraints can not only be applied to JavaBeans and their properties,
but also to the parameters and return values of the methods and constructors of any Java type.
That way Bean Validation constraints can be used to specify
• the preconditions that must be satisfied by the caller before a method or constructor may
be invoked (by applying constraints to the parameters of an executable)
• the postconditions that are guaranteed to the caller after a method or constructor
invocation returns (by applying constraints to the return value of an executable)
For the purpose of this reference guide, the term method constraint refers to
both, method and constructor constraints, if not stated otherwise.
Occasionally, the term executable is used when referring to methods and
constructors.
This approach has several advantages over traditional ways of checking the correctness of
parameters and return values:
In the remainder of this chapter you will learn how to declare parameter and return value
constraints and how to validate them using the ExecutableValidator API.
You specify the preconditions of a method or constructor by adding constraint annotations to its
parameters as demonstrated in Declaring method and constructor parameter constraints.
36
Example 24. Declaring method and constructor parameter constraints
package org.hibernate.validator.referenceguide.chapter03.parameter;
• When invoking the rentCar() method, the given customer must not be null, the rental’s
start date must not be null as well as be in the future and finally the rental duration must
be at least one day
Note that declaring method or constructor constraints itself does not automatically cause their
validation upon invocation of the executable. Instead, the ExecutableValidator API (see
Validating method constraints) must be used to perform the validation, which is often done
using a method interception facility such as AOP, proxy objects etc.
Constraints may only be applied to instance methods, i.e. declaring constraints on static
methods is not supported. Depending on the interception facility you use for triggering method
validation, additional restrictions may apply, e.g. with respect to the visibility of methods
supported as target of interception. Refer to the documentation of the interception technology
to find out whether any such limitations exist.
Cross-parameter constraints
Sometimes validation does not only depend on a single parameter but on several or even all
parameters of a method or constructor. This kind of requirement can be fulfilled with help of a
cross-parameter constraint.
37
In contrast to single-parameter constraints, cross-parameter constraints are declared on the
method or constructor as you can see in Declaring a cross-parameter constraint. Here the
cross- parameter constraint @LuggageCountMatchesPassengerCount declared on the
load() method is used to ensure that no passenger has more than two pieces of luggage.
package org.hibernate.validator.referenceguide.chapter03.crossparameter;
@LuggageCountMatchesPassengerCount(piecesOfLuggagePerPassenger = 2)
public void load(List<Person> passengers, List<PieceOfLuggage>
luggage) {
//...
}
}
As you will learn in the next section, return value constraints are also declared on the method
level. In order to distinguish cross-parameter constraints from return value constraints, the
constraint target is configured in the ConstraintValidator implementation using the
@SupportedValidationTarget annotation. You can find out about the details in Cross-
parameter constraints which shows how to implement your own cross-parameter constraint.
38
Example 26. Specifying a constraint’s target
package
org.hibernate.validator.referenceguide.chapter03.crossparameter.constrain
ttarget;
Although such a constraint is applicable to the parameters and return value of an executable,
the target can often be inferred automatically. This is the case, if the constraint is declared on
• an executable with return value but no parameters (the constraint applies to the return
value)
• neither a method nor a constructor, but a field, parameter etc. (the constraint applies to the
annotated element)
In these situations you don’t have to specify the constraint target. It is still recommended to do
so if it increases readability of the source code. If the constraint target is not specified in
situations where it can’t be determined automatically, a ConstraintDeclarationException is
raised.
39
Example 27. Declaring method and constructor return value constraints
package org.hibernate.validator.referenceguide.chapter03.returnvalue;
@ValidRentalStation
public RentalStation() {
//...
}
@NotNull
@Size(min = 1)
public List<Customer> getCustomers() {
//...
return null;
}
}
• The customer list returned by getCustomers() must not be null and must contain at
least on element
Similar to the cascaded validation of JavaBeans properties (see Object graphs), the @Valid
annotation can be used to mark executable parameters and return values for cascaded
validation. When validating a parameter or return value annotated with @Valid, the constraints
declared on the parameter or return value object are validated as well.
In Marking executable parameters and return values for cascaded validation, the car parameter
of the method Garage#checkCar() as well as the return value of the Garage constructor are
marked for cascaded validation.
40
Example 28. Marking executable parameters and return values for cascaded validation
package org.hibernate.validator.referenceguide.chapter03.cascaded;
@NotNull
private String name;
@Valid
public Garage(String name) {
this.name = name;
}
package org.hibernate.validator.referenceguide.chapter03.cascaded;
@NotNull
private String manufacturer;
@NotNull
@Size(min = 2, max = 14)
private String licensePlate;
When validating the arguments of the checkCar() method, the constraints on the properties of
the passed Car object are evaluated as well. Similarly, the @NotNull constraint on the name
field of Garage is checked when validating the return value of the Garage constructor.
Generally, the cascaded validation works for executables in exactly the same way as it does for
JavaBeans properties.
In particular, null values are ignored during cascaded validation (naturally this can’t happen
during constructor return value validation) and cascaded validation is performed recursively, i.e.
if a parameter or return value object which is marked for cascaded validation itself has
properties marked with @Valid, the constraints declared on the referenced elements will be
validated as well.
41
Cascaded validation can not only be applied to simple object references but also to collection-
typed parameters and return values. This means when putting the @Valid annotation to a
parameter or return value which
• is an array
• implements java.lang.Iterable
• or implements java.util.Map
each contained element gets validated. So when validating the arguments of the checkCars()
method in List-typed method parameter marked for cascaded validation, each element instance
of the passed list will be validated and a ConstraintViolation created when any of the
contained Car instances is invalid.
package
org.hibernate.validator.referenceguide.chapter03.cascaded.collection;
• The postconditions guaranteed to the caller of a method may not be weakened in subtypes
These rules are motivated by the concept of behavioral subtyping which requires that wherever a
type T is used, also a subtype S of T may be used without altering the program’s behavior.
As an example, consider a class invoking a method on an object with the static type T. If the
runtime type of that object was S and S imposed additional preconditions, the client class might
fail to satisfy these preconditions as is not aware of them. The rules of behavioral subtyping are
also known as the Liskov substitution principle.
The Bean Validation specification implements the first rule by disallowing parameter constraints
on methods which override or implement a method declared in a supertype (superclass or
42
interface). Illegal method parameter constraint in subtype shows a violation of this rule.
package
org.hibernate.validator.referenceguide.chapter03.inheritance.parameter;
package
org.hibernate.validator.referenceguide.chapter03.inheritance.parameter;
@Override
public void drive(@Max(55) int speedInMph) {
//...
}
}
The @Max constraint on Car#drive() is illegal since this method implements the interface
method Vehicle#drive(). Note that parameter constraints on overriding methods are also
disallowed, if the supertype method itself doesn’t declare any parameter constraints.
43
Example 31. Illegal method parameter constraint in parallel types of a hierarchy
package
org.hibernate.validator.referenceguide.chapter03.inheritance.parallel;
package
org.hibernate.validator.referenceguide.chapter03.inheritance.parallel;
package
org.hibernate.validator.referenceguide.chapter03.inheritance.parallel;
@Override
public void drive(int speedInMph) {
//...
}
}
The previously described restrictions only apply to parameter constraints. In contrast, return
value constraints may be added in methods overriding or implementing any supertype methods.
In this case, all the method’s return value constraints apply for the subtype method, i.e. the
constraints declared on the subtype method itself as well as any return value constraints on
overridden/implemented supertype methods. This is legal as putting additional return value
constraints in place may never represent a weakening of the postconditions guaranteed to the
caller of a method.
So when validating the return value of the method Car#getPassengers() shown in Return
value constraints on supertype and subtype method, the @Size constraint on the method itself
as well as the @NotNull constraint on the implemented interface method
Vehicle#getPassengers() apply.
44
Example 32. Return value constraints on supertype and subtype method
package
org.hibernate.validator.referenceguide.chapter03.inheritance.returnvalue;
@NotNull
List<Person> getPassengers();
}
package
org.hibernate.validator.referenceguide.chapter03.inheritance.returnvalue;
@Override
@Size(min = 1)
public List<Person> getPassengers() {
//...
return null;
}
}
The rules described in this section only apply to methods but not
constructors. By definition, constructors never override supertype
45
Instead of calling the ExecutableValidator methods directly from within application code,
they are usually invoked via a method interception technology such as AOP, proxy objects, etc.
This causes executable constraints to be validated automatically and transparently upon
method or constructor invocation. Typically a ConstraintViolationException is raised by
the integration layer in case any of the constraints is violated.
In the example the executable validator is retrieved from the default validator factory, but if
required you could also bootstrap a specifically configured factory as described in
Bootstrapping, for instance in order to use a specific parameter name provider (see
ParameterNameProvider).
The examples in the following sections are based on the methods on constructors of the Car
class shown in Class Car with constrained methods and constructors.
46
Example 34. Class Car with constrained methods and constructors
package org.hibernate.validator.referenceguide.chapter03.validation;
@ValidRacingCar
public Car(String manufacturer, String team) {
//...
}
@Size(min = 1)
public List<Passenger> getPassengers() {
//...
return Collections.emptyList();
}
}
ExecutableValidator#validateParameters()
assertEquals( 1, violations.size() );
Class<? extends Annotation> constraintType = violations.iterator()
.next()
.getConstraintDescriptor()
.getAnnotation()
.annotationType();
assertEquals( Max.class, constraintType );
47
Note that validateParameters() validates all the parameter constraints of a method, i.e.
constraints on individual parameters as well as cross-parameter constraints.
ExecutableValidator#validateReturnValue()
Using validateReturnValue() the return value of a method can can be validated. The
validation in Using ExecutableValidator#validateReturnValue() yields one constraint
violation since the getPassengers() method is expect to return at least one Passenger
instance.
assertEquals( 1, violations.size() );
Class<? extends Annotation> constraintType = violations.iterator()
.next()
.getConstraintDescriptor()
.getAnnotation()
.annotationType();
assertEquals( Size.class, constraintType );
ExecutableValidator#validateConstructorParameters()
48
Example 37. Using ExecutableValidator#validateConstructorParameters()
assertEquals( 1, violations.size() );
Class<? extends Annotation> constraintType = violations.iterator()
.next()
.getConstraintDescriptor()
.getAnnotation()
.annotationType();
assertEquals( NotNull.class, constraintType );
ExecutableValidator#validateConstructorReturnValue()
assertEquals( 1, violations.size() );
Class<? extends Annotation> constraintType = violations.iterator()
.next()
.getConstraintDescriptor()
.getAnnotation()
.annotationType();
assertEquals( ValidRacingCar.class, constraintType );
49
3.2.3. ConstraintViolation methods for method validation
All the other ConstraintViolation methods generally work for method validation in the
same way as for validation of beans. Refer to the JavaDoc to learn more about the behavior of
the individual methods and their return values during bean and method validation.
Note that getPropertyPath() can be very useful in order to obtain detailed information about
the validated parameter or return value, e.g. for logging purposes. In particular, you can retrieve
name and argument types of the concerned method as well as the index of the concerned
parameter from the path nodes. How this can be done is shown in Retrieving method and
parameter information.
assertEquals( 1, violations.size() );
Iterator<Node> propertyPath = violations.iterator()
.next()
.getPropertyPath()
.iterator();
50
ParameterNameProvider) and defaults to arg0, arg1 etc.
To refer to the executable’s parameters from within the expression, use their name as obtained
from the active parameter name provider (see ParameterNameProvider). Using
@ParameterScriptAssert shows how the validation logic of the
@LuggageCountMatchesPassengerCount constraint from Declaring a cross-parameter
constraint could be expressed with the help of @ParameterScriptAssert.
package
org.hibernate.validator.referenceguide.chapter03.parameterscriptassert;
51
Chapter 4. Interpolating constraint error
messages
Message interpolation is the process of creating error messages for violated Bean Validation
constraints. In this chapter you will learn how such messages are defined and resolved and how
you can plug in custom message interpolators in case the default algorithm is not sufficient for
your requirements.
package org.hibernate.validator.referenceguide.chapter04;
If a constraint is violated, its descriptor will be interpolated by the validation engine using the
currently configured MessageInterpolator. The interpolated error message can then be
retrieved from the resulting constraint violation by calling
ConstraintViolation#getMessage().
Message descriptors can contain message parameters as well as message expressions which will
be resolved during interpolation. Message parameters are string literals enclosed in {}, while
message expressions are string literals enclosed in ${}. The following algorithm is applied
during method interpolation:
1. Resolve any message parameters by using them as key for the resource bundle
ValidationMessages. If this bundle contains an entry for a given message parameter, that
parameter will be replaced in the message with the corresponding value from the bundle.
This step will be executed recursively in case the replaced value again contains message
parameters. The resource bundle is expected to be provided by the application developer,
e.g. by adding a file named ValidationMessages.properties to the classpath. You can also
52
create localized error messages by providing locale specific variations of this bundle, such
as ValidationMessages_en_US.properties. By default, the JVM’s default locale
(Locale#getDefault()) will be used when looking up messages in the bundle.
2. Resolve any message parameters by using them as key for a resource bundle containing the
standard error messages for the built-in constraints as defined in Appendix B of the Bean
Validation specification. In the case of Hibernate Validator, this bundle is named
org.hibernate.validator.ValidationMessages. If this step triggers a replacement,
step 1 is executed again, otherwise step 3 is applied.
3. Resolve any message parameters by replacing them with the value of the constraint
annotation member of the same name. This allows to refer to attribute values of the
constraint (e.g. Size#min()) in the error message (e.g. "must be at least ${min}").
You can find the formal definition of the interpolation algorithm in section
5.3.1.1 of the Bean Validation specification.
Since the characters {, } and $ have a special meaning in message descriptors they need to be
escaped if you want to use them literally. The following rules apply:
As of Hibernate Validator 5 (Bean Validation 1.1) it is possible to use the Unified Expression
Language (as defined by JSR 341) in constraint violation messages. This allows to define error
messages based on conditional logic and also enables advanced formatting options. The
validation engine makes the following objects available in the EL context:
• the currently validated value (property, bean, method parameter etc.) under the name
validatedValue
• a bean mapped to the name formatter exposing the var-arg method format(String
format, Object… args) which behaves like java.util.Formatter.format(String
format, Object… args).
53
The following section provides several examples for using EL expressions in error messages.
4.1.3. Examples
Specifying message descriptors shows how to make use of the different options for specifying
message descriptors.
54
Example 42. Specifying message descriptors
package org.hibernate.validator.referenceguide.chapter04.complete;
@NotNull
private String manufacturer;
@Size(
min = 2,
max = 14,
message = "The license plate '${validatedValue}' must be
between {min} and {max} characters long"
)
private String licensePlate;
@Min(
value = 2,
message = "There must be at least {value} seat${value > 1 ?
's' : ''}"
)
private int seatCount;
@DecimalMax(
value = "350",
message = "The top speed ${formatter.format('%1$.2f',
validatedValue)} is higher " +
"than {value}"
)
private double topSpeed;
public Car(
String manufacturer,
String licensePlate,
int seatCount,
double topSpeed,
BigDecimal price) {
this.manufacturer = manufacturer;
this.licensePlate = licensePlate;
this.seatCount = seatCount;
this.topSpeed = topSpeed;
this.price = price;
}
Validating an invalid Car instance yields constraint violations with the messages shown by the
assertions in Expected error messages:
55
• the @NotNull constraint on the manufacturer field causes the error message "may not
be null", as this is the default message defined by the Bean Validation specification and no
specific descriptor is given in the message attribute
• the @Size constraint on the licensePlate field shows the interpolation of message
parameters ({min}, {max}) and how to add the validated value to the error message using
the EL expression ${validatedValue}
• the @Min constraint on seatCount demonstrates how use an EL expression with a ternery
expression to dynamically chose singular or plural form, depending on an attribute of the
constraint ("There must be at least 1 seat" vs. "There must be at least 2 seats")
• the message for the @DecimalMax constraint on topSpeed shows how to format the
validated value using the formatter instance
• finally, the @DecimalMax constraint on price shows that parameter interpolation has
precedence over expression evaluation, causing the $ sign to show up in front of the
maximum price
56
Example 43. Expected error messages
57
4.2.1. ResourceBundleLocator
In some use cases you want to use the message interpolation algorithm as defined by the Bean
Validation specification, but retrieve error messages from other resource bundles than
ValidationMessages. In this situation Hibernate Validator’s ResourceBundleLocator SPI can
help.
58
Example 45. Using AggregateResourceBundleLocator
Note that the bundles are processed in the order as passed to the constructor. That means if
several bundles contain an entry for a given message key, the value will be taken from the first
bundle in the list containing the key.
59
Chapter 5. Grouping constraints
All validation methods on Validator and ExecutableValidator discussed in earlier
chapters also take a var-arg argument groups. So far we have been ignoring this parameter, but
it is time to have a closer look.
Let’s have a look at an example. The class Person in Example class Person has a @NotNull
constraint on name. Since no group is specified for this annotation the default group
javax.validation.groups.Default is assumed.
When more than one group is requested, the order in which the groups are
package org.hibernate.validator.referenceguide.chapter05;
@NotNull
private String name;
The class Driver in Driver extends Person and adds the properties age and
hasDrivingLicense. Drivers must be at least 18 years old (@Min(18)) and have a driving
license (@AssertTrue). Both constraints defined on these properties belong to the group
DriverChecks which is just a simple tagging interface.
60
Using interfaces makes the usage of groups type-safe and allows for easy
refactoring. It also means that groups can inherit from each other via class
inheritance. See Group inheritance.
package org.hibernate.validator.referenceguide.chapter05;
@Min(
value = 18,
message = "You have to be 18 to drive a car",
groups = DriverChecks.class
)
public int age;
@AssertTrue(
message = "You first have to pass the driving test",
groups = DriverChecks.class
)
public boolean hasDrivingLicense;
package org.hibernate.validator.referenceguide.chapter05;
Finally the class Car (Car) has some constraints which are part of the default group as well as
@AssertTrue in the group CarChecks on the property passedVehicleInspection which
indicates whether a car passed the road worthy tests.
61
Example 48. Car
package org.hibernate.validator.referenceguide.chapter05;
@NotNull
@Size(min = 2, max = 14)
private String licensePlate;
@Min(2)
private int seatCount;
@AssertTrue(
message = "The car has to pass the vehicle inspection first",
groups = CarChecks.class
)
private boolean passedVehicleInspection;
@Valid
private Driver driver;
package org.hibernate.validator.referenceguide.chapter05;
62
Overall three different groups are used in the example:
Using validation groups shows how passing different group combinations to the
Validator#validate() method results in different validation results.
63
Example 49. Using validation groups
// now let's add a driver. He is 18, but has not passed the driving test
yet
Driver john = new Driver( "John Doe" );
john.setAge( 18 );
car.setDriver( john );
constraintViolations = validator.validate( car, DriverChecks.class );
assertEquals( 1, constraintViolations.size() );
assertEquals(
"You first have to pass the driving test",
constraintViolations.iterator().next().getMessage()
);
The first validate() call in Using validation groups is done using no explicit group. There are
no validation errors, even though the property passedVehicleInspection is per default
false. However, the constraint defined on this property does not belong to the default group.
The next validation using the CarChecks group fails until the car passes the vehicle inspection.
Adding a driver to the car and validating against DriverChecks again yields one constraint
violation due to the fact that the driver has not yet passed the driving test. Only after setting
passedDrivingTest to true the validation against DriverChecks passes.
64
The last validate() call finally shows that all constraints are passing by validating against all
defined groups.
In some situations, you may want to define a group of constraints which includes another group.
You can do that using group inheritance.
In SuperCar, we define a SuperCar and a group RaceCarChecks that extends the Default
group. A SuperCar must have safety belts to be allowed to run in races.
package
org.hibernate.validator.referenceguide.chapter05.groupinheritance;
@AssertTrue(
message = "Race car must have a safety belt",
groups = RaceCarChecks.class
)
private boolean safetyBelt;
package
org.hibernate.validator.referenceguide.chapter05.groupinheritance;
import javax.validation.groups.Default;
In the example below, we will check if a SuperCar with one seat and no security belts is a valid
car and if it is a valid race-car.
65
Example 51. Using group inheritance
// check that this supercar is valid as generic car and also as race car
Set<ConstraintViolation<SuperCar>> constraintViolations = validator
.validate( superCar, RaceCarChecks.class );
On the first call to validate(), we do not specify a group. There is one validation error because
a car must have at least one seat. It is the constraint from the Default group.
On the second call, we specify only the group RaceCarChecks. There are two validation errors:
one about the missing seat from the Default group, another one about the fact that there is no
safety belts coming from the RaceCarChecks group.
In the example from Using validation groups it could for instance be required that first all default
car constraints are passing before checking the road worthiness of the car. Finally, before
driving away, the actual driver constraints should be checked.
In order to implement such a validation order you just need to define an interface and annotate
it with @GroupSequence, defining the order in which the groups have to be validated (see
Defining a group sequence). If at least one constraint fails in a sequenced group none of the
constraints of the following groups in the sequence get validated.
66
Example 52. Defining a group sequence
package org.hibernate.validator.referenceguide.chapter05;
import javax.validation.GroupSequence;
import javax.validation.groups.Default;
You then can use the new sequence as shown in in Using a group sequence.
5.4.1. @GroupSequence
Besides defining group sequences, the @GroupSequence annotation also allows to redefine the
default group for a given class. To do so, just add the @GroupSequence annotation to the class
and specify the sequence of groups which substitute Default for this class within the annotation.
Class RentalCar with redefined default group introduces a new class RentalCar with a
redefined default group.
67
Example 54. Class RentalCar with redefined default group
package org.hibernate.validator.referenceguide.chapter05;
package org.hibernate.validator.referenceguide.chapter05;
With this definition you can evaluate the constraints belonging to RentalChecks, CarChecks
and RentalCar by just requesting the Default group as seen in Validating an object with
redefined default group.
68
Example 55. Validating an object with redefined default group
assertEquals( 1, constraintViolations.size() );
assertEquals(
"Wrong message",
"The car is currently rented out",
constraintViolations.iterator().next().getMessage()
);
rentalCar.setRented( false );
constraintViolations = validator.validate( rentalCar );
assertEquals( 0, constraintViolations.size() );
Since there must no cyclic dependency in the group and group sequence
The Default group sequence overriding is local to the class it is defined on and is not
propagated to associated objects. For the example this means that adding DriverChecks to
the default group sequence of RentalCar would not have any effects. Only the group Default
will be propagated to the driver association.
Note that you can control the propagated group(s) by declaring a group conversion rule (see
Group conversion).
5.4.2. @GroupSequenceProvider
For that purpose you need to implement the interface DefaultGroupSequenceProvider and
register this implementation with the target class via the @GroupSequenceProvider
annotation. In the rental car scenario you could for instance dynamically add the CarChecks as
seen in Implementing and using a default group sequence provider.
69
Example 56. Implementing and using a default group sequence provider
package
org.hibernate.validator.referenceguide.chapter05.groupsequenceprovider;
@Override
public List<Class<?>> getValidationGroups(RentalCar car) {
List<Class<?>> defaultGroupSequence = new ArrayList<Class<?>>();
defaultGroupSequence.add( RentalCar.class );
return defaultGroupSequence;
}
}
package
org.hibernate.validator.referenceguide.chapter05.groupsequenceprovider;
@GroupSequenceProvider(RentalCarGroupSequenceProvider.class)
public class RentalCar extends Car {
70
Let’s have a look at @ConvertGroup usage. Here @GroupSequence({ CarChecks.class,
Car.class }) is used to combine the car related constraints under the Default group (see
Redefining the default group sequence). There is also a @ConvertGroup(from =
Default.class, to = DriverChecks.class) which ensures the Default group gets
converted to the DriverChecks group during cascaded validation of the driver association.
package org.hibernate.validator.referenceguide.chapter05.groupconversion;
@NotNull
private String name;
@Min(
value = 18,
message = "You have to be 18 to drive a car",
groups = DriverChecks.class
)
public int age;
@AssertTrue(
message = "You first have to pass the driving test",
groups = DriverChecks.class
)
public boolean hasDrivingLicense;
71
package org.hibernate.validator.referenceguide.chapter05.groupconversion;
@NotNull
private String manufacturer;
@NotNull
@Size(min = 2, max = 14)
private String licensePlate;
@Min(2)
private int seatCount;
@AssertTrue(
message = "The car has to pass the vehicle inspection first",
groups = CarChecks.class
)
private boolean passedVehicleInspection;
@Valid
@ConvertGroup(from = Default.class, to = DriverChecks.class)
private Driver driver;
As a result the validation in Test case for @ConvertGroup succeeds, even though the constraint
on hasDrivingLicense belongs to the DriverChecks group and only the Default group is
requested in the validate() call.
72
Example 58. Test case for @ConvertGroup
// create a car and validate. The Driver is still null and does not get
validated
Car car = new Car( "VW", "USD-123", 4 );
car.setPassedVehicleInspection( true );
Set<ConstraintViolation<Car>> constraintViolations = validator.validate(
car );
assertEquals( 0, constraintViolations.size() );
You can define group conversions wherever @Valid can be used, namely associations as well as
method and constructor parameters and return values. Multiple conversions can be specified
using @ConvertGroup.List.
• It is not legal to have multiple conversion rules on the same element with the same from
value. In this case, a ConstraintDeclarationException is raised.
Rules are not executed recursively. The first matching conversion rule is
used and subsequent rules are ignored. For example if a set of
@ConvertGroup declarations chains group A to B and B to C, the group A
will be converted to B and not to C.
73
Chapter 6. Creating custom constraints
The Bean Validation API defines a whole set of standard constraint annotations such as
@NotNull, @Size etc. In cases where these built-in constraints are not sufficient, you can easily
create custom constraints tailored to your specific validation requirements.
• Implement a validator
This section shows how to write a constraint annotation which can be used to ensure that a
given string is either completely upper case or lower case. Later on this constraint will be
applied to the licensePlate field of the Car class from Getting started to ensure, that the field
is always an upper-case string.
The first thing needed is a way to express the two case modes. While you could use String
constants, a better approach is using a Java 5 enum for that purpose:
package org.hibernate.validator.referenceguide.chapter06;
The next step is to define the actual constraint annotation. If you’ve never designed an
annotation before, this may look a bit scary, but actually it’s not that hard:
74
Example 60. Defining the @CheckCase constraint annotation
package org.hibernate.validator.referenceguide.chapter06;
CaseMode value();
An annotation type is defined using the @interface keyword. All attributes of an annotation
type are declared in a method-like manner. The specification of the Bean Validation API
demands, that any constraint annotation defines
• an attribute message that returns the default key for creating error messages in case the
constraint is violated
• an attribute groups that allows the specification of validation groups, to which this
constraint belongs (see Grouping constraints). This must default to an empty array of type
Class<?>.
• an attribute payload that can be used by clients of the Bean Validation API to assign
custom payload objects to a constraint. This attribute is not used by the API itself. An
example for a custom payload could be the definition of a severity:
75
public class Severity {
public interface Info extends Payload {
}
// ...
}
Now a client can after the validation of a ContactDetails instance access the severity of
a constraint using
ConstraintViolation.getConstraintDescriptor().getPayload() and adjust its
behavior depending on the severity.
Besides these three mandatory attributes there is another one, value, allowing for the required
case mode to be specified. The name value is a special one, which can be omitted when using
the annotation, if it is the only attribute specified, as e.g. in @CheckCase(CaseMode.UPPER).
When creating a class-level constraint (see Class-level constraints), the element type TYPE
would have to be used. Constraints targeting the return value of a constructor need to
support the element type CONSTRUCTOR. Cross-parameter constraints (see Cross-
parameter constraints) which are used to validate all the parameters of a method or
constructor together, must support METHOD or CONSTRUCTOR, respectively.
76
• @Retention(RUNTIME): Specifies, that annotations of this type will be available at runtime
by the means of reflection
• @Documented: Says, that the use of @CheckCase will be contained in the JavaDoc of
elements annotated with it
Finally, there is an inner annotation type named List. This annotation allows to specify several
@CheckCase annotations on the same element, e.g. with different validation groups and
messages. While also another name could be used, the Bean Validation specification
recommends to use the name List and make the annotation an inner annotation of the
corresponding constraint type.
Having defined the annotation, you need to create a constraint validator, which is able to
validate elements with a @CheckCase annotation. To do so, implement the interface
ConstraintValidator as shown below:
77
Example 61. Implementing a constraint validator for the constraint @CheckCase
package org.hibernate.validator.referenceguide.chapter06;
@Override
public void initialize(CheckCase constraintAnnotation) {
this.caseMode = constraintAnnotation.value();
}
@Override
public boolean isValid(String object, ConstraintValidatorContext
constraintContext) {
if ( object == null ) {
return true;
}
if ( caseMode == CaseMode.UPPER ) {
return object.equals( object.toUpperCase() );
}
else {
return object.equals( object.toLowerCase() );
}
}
}
The ConstraintValidator interface defines two type parameters which are set in the
implementation. The first one specifies the annotation type to be validated (CheckCase), the
second one the type of elements, which the validator can handle (String). In case a constraint
supports several data types, a ConstraintValidator for each allowed type has to be
implemented and registered at the constraint annotation as shown above.
The implementation of the validator is straightforward. The initialize() method gives you
access to the attribute values of the validated constraint and allows you to store them in a field
of the validator as shown in the example.
The isValid() method contains the actual validation logic. For @CheckCase this is the check
whether a given string is either completely lower case or upper case, depending on the case
mode retrieved in initialize(). Note that the Bean Validation specification recommends to
consider null values as being valid. If null is not a valid value for an element, it should be
annotated with @NotNull explicitly.
The ConstraintValidatorContext
Implementing a constraint validator for the constraint @CheckCase relies on the default error
message generation by just returning true or false from the isValid() method. Using the
78
passed ConstraintValidatorContext object it is possible to either add additional error
messages or completely disable the default error message generation and solely define custom
error messages. The ConstraintValidatorContext API is modeled as fluent interface and is
best demonstrated with an example:
package
org.hibernate.validator.referenceguide.chapter06.constraintvalidatorconte
xt;
@Override
public void initialize(CheckCase constraintAnnotation) {
this.caseMode = constraintAnnotation.value();
}
@Override
public boolean isValid(String object, ConstraintValidatorContext
constraintContext) {
if ( object == null ) {
return true;
}
boolean isValid;
if ( caseMode == CaseMode.UPPER ) {
isValid = object.equals( object.toUpperCase() );
}
else {
isValid = object.equals( object.toLowerCase() );
}
if ( !isValid ) {
constraintContext.disableDefaultConstraintViolation();
constraintContext.buildConstraintViolationWithTemplate(
"{org.hibernate.validator.referenceguide.chapter06."
+
"constraintvalidatorcontext.CheckCase.message}"
)
.addConstraintViolation();
}
return isValid;
}
}
Using ConstraintValidatorContext to define custom error messages shows how you can
disable the default error message generation and add a custom error message using a specified
message template. In this example the use of the ConstraintValidatorContext results in
the same error message as the default error message generation.
79
It is important to add each configured constraint violation by calling
Refer to Custom property paths to learn how to use the ConstraintValidatorContext API
to control the property path of constraint violations for class-level constraints.
The last missing building block is an error message which should be used in case a @CheckCase
constraint is violated. To define this, create a file ValidationMessages.properties with the
following contents (see also Default message interpolation):
Example 63. Defining a custom error message for the CheckCase constraint
org.hibernate.validator.referenceguide.chapter06.CheckCase.message=Case
mode must be {value}.
If a validation error occurs, the validation runtime will use the default value, that you specified
for the message attribute of the @CheckCase annotation to look up the error message in this
resource bundle.
You can now use the constraint in the Car class from the Getting started chapter to specify that
the licensePlate field should only contain upper-case strings:
80
Example 64. Applying the @CheckCase constraint
package org.hibernate.validator.referenceguide.chapter06;
@NotNull
private String manufacturer;
@NotNull
@Size(min = 2, max = 14)
@CheckCase(CaseMode.UPPER)
private String licensePlate;
@Min(2)
private int seatCount;
Finally, Validating objects with the @CheckCase constraint demonstrates how validating a Car
instance with an invalid license plate causes the @CheckCase constraint to be violated.
assertEquals( 0, constraintViolations.size() );
81
6.2. Class-level constraints
As discussed earlier, constraints can also be applied on the class level to validate the state of an
entire object. Class-level constraints are defined in the same was as are property constraints.
Implementing a class-level constraint shows constraint annotation and validator of the
@ValidPassengerCount constraint you already saw in use in Class-level constraint.
package org.hibernate.validator.referenceguide.chapter06.classlevel;
package org.hibernate.validator.referenceguide.chapter06.classlevel;
@Override
public void initialize(ValidPassengerCount constraintAnnotation) {
}
@Override
public boolean isValid(Car car, ConstraintValidatorContext context) {
if ( car == null ) {
return true;
}
As the example demonstrates, you need to use the element type TYPE in the @Target
annotation. This allows the constraint to be put on type definitions. The validator of the
constraint in the example receives a Car in the isValid() method and can access the
complete object state to decide whether the given instance is valid or not.
82
6.2.1. Custom property paths
By default the constraint violation for a class-level constraint is reported on the level of the
annotated type, e.g. Car.
In some cases it is preferable though that the violation’s property path refers to one of the
involved properties. For instance you might want to report the @ValidPassengerCount
constraint against the passengers property instead of the Car bean.
Adding a new ConstraintViolation with custom property path shows how this can be done
by using the constraint validator context passed to isValid() to build a custom constraint
violation with a property node for the property passengers. Note that you also could add several
property nodes, pointing to a sub-entity of the validated bean.
package org.hibernate.validator.referenceguide.chapter06.custompath;
@Override
public void initialize(ValidPassengerCount constraintAnnotation) {
}
@Override
public boolean isValid(Car car, ConstraintValidatorContext
constraintValidatorContext) {
if ( car == null ) {
return true;
}
if ( !isValid ) {
constraintValidatorContext.disableDefaultConstraintViolation
();
constraintValidatorContext
.buildConstraintViolationWithTemplate(
"{my.custom.template}" )
.addPropertyNode( "passengers" )
.addConstraintViolation();
}
return isValid;
}
}
83
6.3. Cross-parameter constraints
Bean Validation distinguishes between two different kinds of constraints.
Generic constraints (which have been discussed so far) apply to the annotated element, e.g. a
type, field, method parameter or return value etc. Cross-parameter constraints, in contrast,
apply to the array of parameters of a method or constructor and can be used to express
validation logic which depends on several parameter values.
In order to define a cross-parameter constraint, its validator class must be annotated with
@SupportedValidationTarget(ValidationTarget.PARAMETERS). The type parameter T
from the ConstraintValidator interface must resolve to either Object or Object[] in
order to receive the array of method/constructor arguments in the isValid() method.
The following example shows the definition of a cross-parameter constraint which can be used
to check that two Date parameters of a method are in the correct order:
package org.hibernate.validator.referenceguide.chapter06.crossparameter;
@Constraint(validatedBy = ConsistentDateParametersValidator.class)
@Target({ METHOD, CONSTRUCTOR, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Documented
public @interface ConsistentDateParameters {
The definition of a cross-parameter constraint isn’t any different from defining a generic
constraint, i.e. it must specify the members message(), groups() and payload() and be
annotated with @Constraint. This meta annotation also specifies the corresponding validator,
which is shown in Generic and cross-parameter constraint. Note that besides the element types
METHOD and CONSTRUCTOR also ANNOTATION_TYPE is specified as target of the annotation, in
order to enable the creation of composed constraints based on @ConsistentDateParameters
(see Constraint composition).
84
Cross-parameter constraints are specified directly on the declaration of a
method or constructor, which is also the case for return value constraints. In
package org.hibernate.validator.referenceguide.chapter06.crossparameter;
@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class ConsistentDateParametersValidator implements
ConstraintValidator<ConsistentDateParameters, Object[]> {
@Override
public void initialize(ConsistentDateParameters constraintAnnotation)
{
}
@Override
public boolean isValid(Object[] value, ConstraintValidatorContext
context) {
if ( value.length != 2 ) {
throw new IllegalArgumentException( "Illegal method
signature" );
}
As discussed above, the validation target PARAMETERS must be configured for a cross-
parameter validator by using the @SupportedValidationTarget annotation. Since a cross-
parameter constraint could be applied to any method or constructor, it is considered a best
practice to check for the expected number and types of parameters in the validator
implementation.
As with generic constraints, null parameters should be considered valid and @NotNull on the
85
individual parameters should be used to make sure that parameters are not null.
In rare situations a constraint is both, generic and cross-parameter. This is the case if a
constraint has a validator class which is annotated with
@SupportedValidationTarget({ValidationTarget.PARAMETERS,
ValidationTarget.ANNOTATED_ELEMENT}) or if it has a generic and a cross-parameter
validator class.
When declaring such a constraint on a method which has parameters and also a return value, the
intended constraint target can’t be determined. Constraints which are generic and cross-
parameter at the same time, must therefore define a member validationAppliesTo() which
allows the constraint user to specify the constraint’s target as shown in Generic and cross-
parameter constraint.
package org.hibernate.validator.referenceguide.chapter06.crossparameter;
@Constraint(validatedBy = {
ScriptAssertObjectValidator.class,
ScriptAssertParametersValidator.class
})
@Target({ TYPE, FIELD, PARAMETER, METHOD, CONSTRUCTOR, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Documented
public @interface ScriptAssert {
String script();
86
The @ScriptAssert constraint has two validators (not shown), a generic and a cross-
parameter one and thus defines the member validationAppliesTo(). The default value
IMPLICIT allows to derive the target automatically in situations where this is possible (e.g. if
the constraint is declared on a field or on a method which has parameters but no return value).
If the target can not be determined implicitly, it must be set by the user to either PARAMETERS
or RETURN_VALUE as shown in Specifying the target for a generic and cross-parameter
constraint.
Example 71. Specifying the target for a generic and cross-parameter constraint
You can address this kind of problem by creating higher level constraints, composed from
several basic constraints. Creating a composing constraint @ValidLicensePlate shows a
composed constraint annotation which comprises the constraints @NotNull, @Size and
@CheckCase:
87
Example 72. Creating a composing constraint @ValidLicensePlate
package
org.hibernate.validator.referenceguide.chapter06.constraintcomposition;
@NotNull
@Size(min = 2, max = 14)
@CheckCase(CaseMode.UPPER)
@Target({ METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = { })
@Documented
public @interface ValidLicensePlate {
To create a composed constraint, simply annotate the constraint declaration with its comprising
constraints. If the composed constraint itself requires a validator, this validator is to be specified
within the @Constraint annotation. For composed constraints which don’t need an additional
validator such as @ValidLicensePlate, just set validatedBy() to an empty array.
Using the new composed constraint at the licensePlate field is fully equivalent to the
previous version, where the three constraints were declared directly at the field itself:
package
org.hibernate.validator.referenceguide.chapter06.constraintcomposition;
@ValidLicensePlate
private String licensePlate;
//...
}
The set of ConstraintViolations retrieved when validating a Car instance will contain an
entry for each violated composing constraint of the @ValidLicensePlate constraint. If you
rather prefer a single ConstraintViolation in case any of the composing constraints is
violated, the @ReportAsSingleViolation meta constraint can be used as follows:
88
Example 74. Using @ReportAsSingleViolation
package
org.hibernate.validator.referenceguide.chapter06.constraintcomposition.re
portassingle;
//...
@ReportAsSingleViolation
public @interface ValidLicensePlate {
89
Chapter 7. Configuring via XML
So far we have used the default configuration source for Bean Validation, namely annotations.
However, there also exist two kinds of XML descriptors allowing configuration via XML. The first
descriptor describes general Bean Validation behaviour and is provided as META-
INF/validation.xml. The second one describes constraint declarations and closely matches the
constraint declaration approach via annotations. Let’s have a look at these two document types.
http://www.jboss.org/xml/ns/javax/validation/configuration and
http://www.jboss.org/xml/ns/javax/validation/mapping.
90
Figure 1. Validation configuration schema
validation.xml shows the several configuration options of validation.xml. All settings are
optional and the same configuration options are also available programmatically through
javax.validation.Configuration. In fact the XML configuration will be overridden by
values explicitly specified via the programmatic API. It is even possible to ignore the XML
configuration completely via Configuration#ignoreXmlConfiguration(). See also
91
Configuring a ValidatorFactory.
<validation-config
xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://jboss.org/xml/ns/javax/validation/configuration">
<default-provider>com.acme.ValidationProvider</default-provider>
<message-interpolator>com.acme.MessageInterpolator</message-
interpolator>
<traversable-resolver>com.acme.TraversableResolver</traversable-
resolver>
<constraint-validator-factory>
com.acme.ConstraintValidatorFactory
</constraint-validator-factory>
<parameter-name-provider>com.acme.ParameterNameProvider</parameter-
name-provider>
<executable-validation enabled="true">
<default-validated-executable-types>
<executable-type>CONSTRUCTORS</executable-type>
<executable-type>NON_GETTER_METHODS</executable-type>
<executable-type>GETTER_METHODS</executable-type>
</default-validated-executable-types>
</executable-validation>
<constraint-mapping>META-INF/validation/constraints-
car.xml</constraint-mapping>
<property name="hibernate.validator.fail_fast">false</property>
</validation-config>
The node default-provider allows to choose the Bean Validation provider. This is useful if
there is more than one provider on the classpath. message-interpolator, traversable-
resolver, constraint-validator-factory and parameter-name-provider allow to
customize the used implementations for the interfaces MessageInterpolator,
TraversableResolver, ConstraintValidatorFactory and ParameterNameProvider
defined in the javax.validation package. See the sub-sections of Configuring a
ValidatorFactory for more information about these interfaces.
executable-validation and its subnodes define defaults for method validation. The Bean
Validation specification defines constructor and non getter methods as defaults. The enabled
attribute acts as global switch to turn method validation on and off (see also Declaring and
validating method constraints).
92
Via the constraint-mapping element you can list an arbitrary number of additional XML files
containing the actual constraint configuration. Mapping file names must be specified using their
fully-qualified name on the classpath. Details on writing mapping files can be found in the next
section.
Last but not least, you can specify provider specific properties via the property nodes. In the
example we are using the Hibernate Validator specific hibernate.validator.fail_fast
property (see Fail fast mode).
93
94
Figure 2. Validation mapping schema
Bean constraints configured via XML shows how the classes Car and RentalCar from Car resp.
Class RentalCar with redefined default group could be mapped in XML.
95
Example 76. Bean constraints configured via XML
<constraint-mappings
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/mapping
validation-mapping-1.1.xsd"
xmlns="http://jboss.org/xml/ns/javax/validation/mapping" version="
1.1">
<default-package>
org.hibernate.validator.referenceguide.chapter05</default-package>
<bean class="Car" ignore-annotations="true">
<field name="manufacturer">
<constraint annotation="javax.validation.constraints.NotNull
"/>
</field>
<field name="licensePlate">
<constraint annotation="javax.validation.constraints.NotNull
"/>
</field>
<field name="seatCount">
<constraint annotation="javax.validation.constraints.Min">
<element name="value">2</element>
</constraint>
</field>
<field name="driver">
<valid/>
</field>
<getter name="passedVehicleInspection" ignore-annotations="true">
<constraint annotation=
"javax.validation.constraints.AssertTrue">
<message>The car has to pass the vehicle inspection
first</message>
<groups>
<value>CarChecks</value>
</groups>
<element name="max">10</element>
</constraint>
</getter>
</bean>
<bean class="RentalCar" ignore-annotations="true">
<class ignore-annotations="true">
<group-sequence>
<value>RentalCar</value>
<value>CarChecks</value>
</group-sequence>
</class>
</bean>
<constraint-definition annotation="org.mycompany.CheckCase">
<validated-by include-existing-validators="false">
<value>org.mycompany.CheckCaseValidator</value>
</validated-by>
</constraint-definition>
</constraint-mappings>
Method constraints configured via XML shows how the constraints from Declaring method and
96
constructor parameter constraints, Declaring method and constructor return value constraints
and Specifying a constraint’s target can be expressed in XML.
<constraint-mappings
xmlns="http://jboss.org/xml/ns/javax/validation/mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://jboss.org/xml/ns/javax/validation/mapping
validation-mapping-1.1.xsd" version="1.1">
<default-package>
org.hibernate.validator.referenceguide.chapter07</default-package>
<constructor>
<parameter type="java.lang.String">
<constraint annotation=
"javax.validation.constraints.NotNull"/>
</parameter>
</constructor>
<method name="getCustomers">
<return-value>
<constraint annotation=
"javax.validation.constraints.NotNull"/>
<constraint annotation="
javax.validation.constraints.Size">
<element name="min">1</element>
</constraint>
</return-value>
</method>
<method name="rentCar">
<parameter type="Customer">
<constraint annotation=
"javax.validation.constraints.NotNull"/>
</parameter>
<parameter type="java.util.Date">
<constraint annotation=
"javax.validation.constraints.NotNull"/>
<constraint annotation=
"javax.validation.constraints.Future"/>
</parameter>
<parameter type="int">
<constraint annotation="javax.validation.constraints.Min
">
<element name="value">1</element>
</constraint>
</parameter>
</method>
97
</bean>
</constraint-mappings>
The XML configuration is closely mirroring the programmatic API. For this reason it should
suffice to just add some comments. default-package is used for all fields where a class name
is expected. If the specified class is not fully qualified the configured default package will be
used. Every mapping file can then have several bean nodes, each describing the constraints on
the entity with the specified class name.
A given class can only be configured once across all configuration files. The
same applies for constraint definitions for a given constraint annotation. It
can only occur in one mapping file. If these rules are violated a
ValidationException is thrown.
The nodes class, field, getter, constructor and method (and its sub node parameter)
determine on which level the constraint gets placed. The constraint node is then used to add
a constraint on the corresponding level. Each constraint definition must define the class via the
annotation attribute. The constraint attributes required by the Bean Validation specification
(message, groups and payload) have dedicated nodes. All other constraint specific attributes
are configured using the element node.
98
The class node also allows to reconfigure the default group sequence (see Redefining the
default group sequence) via the group-sequence node. Not shown in the example is the use of
convert-group to specify group conversions (see Group conversion). This node is available on
field, getter, parameter and return-value and specifies a from and to attribute to specify
the groups.
Last but not least, the list of ConstraintValidator instances associated to a given constraint
can be altered via the constraint-definition node. The annotation attribute represents the
constraint annotation being altered. The validated-by element represent the (ordered) list of
ConstraintValidator implementations associated to the constraint. If include-existing-
validator is set to false, validators defined on the constraint annotation are ignored. If set to
true, the list of constraint validators described in XML is concatenated to the list of validators
specified on the annotation.
Using XML to register a regular expression based constraint definition for @URL
<constraint-definition annotation=
"org.hibernate.validator.constraints.URL">
<validated-by include-existing-validators="false">
<value>org.hibernate.validator.constraintvalidators.RegexpURL
Validator</value>
</validated-by>
</constraint-definition>
99
Chapter 8. Bootstrapping
In Obtaining a Validator instance you already saw one way for creating a Validator instance -
via Validation#buildDefaultValidatorFactory(). In this chapter you will learn how to
use the other methods in javax.validation.Validation in order to bootstrap specifically
configured validators.
Bean Validation supports working with several providers such as Hibernate Validator within one
application. If more than one provider is present on the classpath, it is not guaranteed which one
is chosen when creating a factory via buildDefaultValidatorFactory().
In this case you can explicitly specify the provider to use via Validation#byProvider(),
passing the provider’s ValidationProvider class as shown in Bootstrapping
ValidatorFactory and Validator using a specific provider.
100
Note that the configuration object returned by configure() allows to specifically customize
the factory before calling buildValidatorFactory(). The available options are discussed
later in this chapter.
Similarly you can retrieve the default validator factory for configuration which is demonstrated
in Retrieving the default ValidatorFactory for configuration.
8.1.1. ValidationProviderResolver
By default, available Bean Validation providers are discovered using the Java Service Provider
mechanism.
Depending on your environment and its classloading specifics, provider discovery via the Java’s
service loader mechanism might not work. In this case you can plug in a custom
ValidationProviderResolver implementation which performs the provider retrieval. An
example is OSGi, where you could implement a provider resolver which uses OSGi services for
provider discovery.
To use a custom provider resolver pass it via providerResolver() as shown shown in Using a
custom ValidationProviderResolver.
101
Example 81. Using a custom ValidationProviderResolver
package org.hibernate.validator.referenceguide.chapter08;
@Override
public List<ValidationProvider<?>> getValidationProviders() {
//...
return null;
}
}
If you want to disable the XML based configuration, you can do so by invoking
Configuration#ignoreXmlConfiguration().
Using the fluent configuration API, you can override one or more of the settings when
bootstrapping the factory. The following sections show how to make use of the different options.
Note that the Configuration class exposes the default implementations of the different
extension points which can be useful if you want to use these as delegates for your custom
implementations.
8.2.1. MessageInterpolator
Message interpolators are used by the validation engine to create user readable error messages
from constraint message descriptors.
102
In case the default message interpolation algorithm described in Interpolating constraint error
messages is not sufficient for your needs, you can pass in your own implementation of the
MessageInterpolator interface via Configuration#messageInterpolator() as shown
in Using a custom MessageInterpolator.
package org.hibernate.validator.referenceguide.chapter08;
@Override
public String interpolate(String messageTemplate, Context context) {
//...
return null;
}
@Override
public String interpolate(String messageTemplate, Context context,
Locale locale) {
//...
return null;
}
}
8.2.2. TraversableResolver
In some cases the validation engine should not access the state of a bean property. The most
obvious example for that is a lazily loaded property or association of a JPA entity. Validating this
lazy property or association would mean that its state would have to be accessed, triggering a
load from the database.
Which properties can be accessed and which ones not is controlled by querying the
TraversableResolver interface. Using a custom TraversableResolver shows how to use a
custom traversable resolver implementation.
103
Example 83. Using a custom TraversableResolver
package org.hibernate.validator.referenceguide.chapter08;
@Override
public boolean isReachable(
Object traversableObject,
Node traversableProperty,
Class<?> rootBeanType,
Path pathToTraversableObject,
ElementType elementType) {
//...
return false;
}
@Override
public boolean isCascadable(
Object traversableObject,
Node traversableProperty,
Class<?> rootBeanType,
Path pathToTraversableObject,
ElementType elementType) {
//...
return false;
}
}
If no specific traversable resolver has been configured, the default behavior is to consider all
properties as reachable and cascadable. When using Hibernate Validator together with a JPA 2
provider such as Hibernate ORM, only those properties will be considered reachable which
already have been loaded by the persistence provider and all properties will be considered
cascadable.
8.2.3. ConstraintValidatorFactory
104
to use dependency injection in constraint validator implementations.
package org.hibernate.validator.referenceguide.chapter08;
@Override
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T>
key) {
//...
return null;
}
@Override
public void releaseInstance(ConstraintValidator<?, ?> instance) {
//...
}
}
8.2.4. ParameterNameProvider
105
The default implementation returns parameter names in the form of arg0, arg1 etc, while
custom implementations can retrieve the parameter names using methods such as parameter
annotations, debug symbols, or Java 8 reflection.
An implementation for retrieving the parameter names using reflection in Java 8 is provided
with ReflectionParameterNameProvider. For this parameter name provider to work, the
source must be compiled using the –parameters compiler argument. Otherwise, the provider
will return synthetic names in the form of arg0, arg1, etc.
package org.hibernate.validator.referenceguide.chapter08;
@Override
public List<String> getParameterNames(Constructor<?> constructor) {
//...
return null;
}
@Override
public List<String> getParameterNames(Method method) {
//...
return null;
}
}
106
8.2.5. Adding mapping streams
As discussed earlier you can configure the constraints applying for your Java beans using XML
based constraint mappings.
Besides the mapping files specified in META-INF/validation.xml you can add further mappings
via Configuration#addMapping() (see Adding constraint mapping streams). Note that the
passed input stream(s) must adhere to the XML schema for constraint mappings presented in
Mapping constraints via constraint-mappings.
You should close any passed input stream after the validator factory has been created.
In case of Hibernate Validator this e.g. allows you to enable the fail fast mode and pass one or
more programmatic constraint mappings as demonstrated in Setting Hibernate Validator
specific options.
107
Example 88. Enabling a Hibernate Validator specific option via addProperty()
Refer to Fail fast mode and Programmatic constraint definition and declaration to learn more
about the fail fast mode and the constraint declaration API.
108
Chapter 9. Using constraint metadata
The Bean Validation specification provides not only a validation engine, but also an API for
retrieving constraint metadata in a uniform way, no matter whether the constraints are declared
using annotations or via XML mappings. Read this chapter to learn more about this API and its
possibilities. You can find all the metadata API types in the package
javax.validation.metadata.
The examples presented in this chapter are based on the classes and constraint declarations
shown in Example classes.
package org.hibernate.validator.referenceguide.chapter09;
@NotNull
private String name;
package org.hibernate.validator.referenceguide.chapter09;
@NotNull(groups = Vehicle.Basic.class)
String getManufacturer();
}
package org.hibernate.validator.referenceguide.chapter09;
@ValidCar
public class Car implements Vehicle {
@NotNull
@Size(min = 2, max = 14)
private String licensePlate;
109
private String modelName;
public Car() {
}
public Car(
@NotNull String manufacturer,
String licencePlate,
Person driver,
String modelName) {
this.manufacturer = manufacturer;
this.licensePlate = licencePlate;
this.driver = driver;
this.modelName = modelName;
}
@LuggageCountMatchesPassengerCount(
piecesOfLuggagePerPassenger = 2,
validationAppliesTo = ConstraintTarget.PARAMETERS,
payload = SeverityInfo.class,
message = "There must not be more than
{piecesOfLuggagePerPassenger} pieces " +
"of luggage per passenger."
)
public void load(List<Person> passengers, List<PieceOfLuggage>
luggage) {
//...
}
@Override
@Size(min = 3)
public String getManufacturer() {
return manufacturer;
}
@Valid
@ConvertGroup(from = Default.class, to = Person.Basic.class)
public Person getDriver() {
return driver;
}
9.1. BeanDescriptor
The entry point into the metadata API is the method
110
Validator#getConstraintsForClass(), which returns an instance of the
BeanDescriptor interface. Using this descriptor, you can obtain metadata for constraints
declared directly on the bean itself (class- or property-level), but also retrieve metadata
descriptors representing single properties, methods and constructors.
Using BeanDescriptor demonstrates how to retrieve a BeanDescriptor for the Car class
and how to use this descriptor in form of assertions.
assertTrue( carDescriptor.isBeanConstrained() );
111
//driveAway(int)
assertNotNull( carDescriptor.getConstraintsForMethod( "driveAway", int
.class ) );
//getManufacturer()
assertNotNull( carDescriptor.getConstraintsForMethod( "getManufacturer" )
);
You can determine whether the specified class hosts any class- or property-level constraints via
isBeanConstrained(). Method or constructor constraints are not considered by
isBeanConstrained().
Note that these methods consider constraints declared at super-types according to the rules for
constraint inheritance as described in Constraint inheritance. An example is the descriptor for
the manufacturer property, which provides access to all constraints defined on
Vehicle#getManufacturer() and the implementing method Car#getManufacturer().
null is returned in case the specified element does not exist or is not constrained.
112
has at least one constraint or is marked for cascaded validation. When invoking
getConstrainedMethods(), you can specify the type of the methods to be returned (getters,
non-getters or both).
9.2. PropertyDescriptor
The interface PropertyDescriptor represents one given property of a class. It is transparent
whether constraints are declared on a field or a property getter, provided the JavaBeans naming
conventions are respected. Using PropertyDescriptor shows how to use the
PropertyDescriptor interface.
113
9.3. MethodDescriptor and ConstructorDescriptor
Constrained methods and constructors are represented by the interfaces MethodDescriptor
and ConstructorDescriptor, respectively. Using MethodDescriptor and
ConstructorDescriptor demonstrates how to work with these descriptors.
114
"load",
List.class,
List.class
);
assertTrue( loadDescriptor.hasConstrainedParameters() );
assertFalse( loadDescriptor.hasConstrainedReturnValue() );
assertEquals(
1,
loadDescriptor.getCrossParameterDescriptor()
.getConstraintDescriptors().size()
);
getName() returns the name of the given method or constructor. The methods
hasConstrainedParameters() and hasConstrainedReturnValue() can be used to
perform a quick check whether an executable element has any parameter constraints (either
constraints on single parameters or cross-parameter constraints) or return value constraints.
Note that any constraints are not directly exposed on MethodDescriptor and
ConstructorDescriptor, but rather on dedicated descriptors representing an executable’s
parameters, its return value and its cross-parameter constraints. To get hold of one of these
descriptors, invoke getParameterDescriptors(), getReturnValueDescriptor() or
getCrossParameterDescriptor(), respectively.
115
Getter methods following the JavaBeans naming conventions are
considered as bean properties but also as constrained methods.
That means you can retrieve the related metadata either by obtaining a
PropertyDescriptor (e.g.
BeanDescriptor.getConstraintsForProperty("foo")) or by
examining the return value descriptor of the getter’s MethodDescriptor
(e.g.
BeanDescriptor.getConstraintsForMethod("getFoo").getReturn
ValueDescriptor()).
9.4. ElementDescriptor
The ElementDiscriptor interface is the common base class for the individual descriptor
types such as BeanDescriptor, PropertyDescriptor etc. Besides
getConstraintDescriptors() it provides some more methods common to all descriptors.
hasConstraints() allows for a quick check whether an element has any direct constraints
(e.g. class- level constraints in case of BeanDescriptor). getElementClass() returns the
Java type of the element represented by a given descriptor. More specifically, the method
returns
116
Example 94. Using ElementDescriptor methods
assertTrue( manufacturerDescriptor.hasConstraints() );
assertEquals( String.class, manufacturerDescriptor.getElementClass() );
assertTrue( loadCrossParameterDescriptor.hasConstraints() );
assertEquals( Object[].class, loadCrossParameterDescriptor
.getElementClass() );
Finally, ElementDescriptor offers access to the ConstraintFinder API which allows you to
query for constraint metadata in a fine grained way. Usage of ConstraintFinder shows how
to retrieve a ConstraintFinder instance via findConstraints() and use the API to query
for constraint metadata.
117
Example 95. Usage of ConstraintFinder
//@Size on Car#getManufacturer()
assertEquals(
1,
manufacturerDescriptor.findConstraints()
.lookingAt( Scope.LOCAL_ELEMENT )
.getConstraintDescriptors()
.size()
);
Via declaredOn() you can search for ConstraintDescriptors declared on certain element
types. This is useful to find property constraints declared on either fields or getter methods.
118
unorderedAndMatchingGroups() restricts the resulting constraints to those matching the
given validation group(s).
You can also combine the different options as shown in the last example.
9.5. GroupConversionDescriptor
All those descriptor types that represent elements which can be subject of cascaded validation
(i.e., PropertyDescriptor, ParameterDescriptor and ReturnValueDescriptor) provide
access to the element’s group conversions via getGroupConversions(). The returned set
contains a GroupConversionDescriptor for each configured conversion, allowing to retrieve
source and target groups of the conversion. Using GroupConversionDescriptor shows an
example.
9.6. ConstraintDescriptor
Last but not least, the ConstraintDescriptor interface describes a single constraint
together with its composing constraints. Via an instance of this interface you get access to the
constraint annotation and its parameters.
119
type and its validators from a ConstraintDescriptor.
//constraint type
assertEquals(
LuggageCountMatchesPassengerCount.class,
constraintDescriptor.getAnnotation().annotationType()
);
//validator class
assertEquals(
Arrays.<Class<?>>asList( LuggageCountMatchesPassengerCount
.Validator.class ),
constraintDescriptor.getConstraintValidatorClasses()
);
120
Chapter 10. Integrating with other
frameworks
Hibernate Validator is intended to be used to implement multi-layered data validation, where
constraints are expressed in a single place (the annotated domain model) and checked in
various different layers of the application. For this reason there are multiple integration points
with other technologies.
Out of the box, Hibernate (as of version 3.5.x) will translate the constraints you have defined for
your entities into mapping metadata. For example, if a property of your entity is annotated
@NotNull, its columns will be declared as not null in the DDL schema generated by
Hibernate.
You can also limit the DDL constraint generation to a subset of the defined constraints by
setting the property org.hibernate.validator.group.ddl. The property specifies the
comma-separated, fully specified class names of the groups a constraint has to be part of in
order to be considered for DDL schema generation.
121
made by Hibernate. Pre deletion events will per default not trigger a validation. You can
configure the groups to be validated per event type using the properties
javax.persistence.validation.group.pre-persist,
javax.persistence.validation.group.pre-update and
javax.persistence.validation.group.pre-remove. The values of these properties are
the comma-separated, fully specified class names of the groups to validate. Manual
configuration of BeanValidationEvenListener shows the default values for these
properties. In this case they could also be omitted.
If Hibernate Validator is present in the classpath, Hibernate ORM will use it transparently. To
avoid validation even though Hibernate Validator is in the classpath set
javax.persistence.validation.mode to none.
In case you need to manually set the event listeners for Hibernate ORM, use the following
configuration in hibernate.cfg.xml:
122
Example 98. Manual configuration of BeanValidationEvenListener
<hibernate-configuration>
<session-factory>
...
<property name="javax.persistence.validation.group.pre-persist">
javax.validation.groups.Default
</property>
<property name="javax.persistence.validation.group.pre-update">
javax.validation.groups.Default
</property>
<property name="javax.persistence.validation.group.pre-remove"
></property>
...
<event type="pre-update">
<listener class=
"org.hibernate.cfg.beanvalidation.BeanValidationEventListener"/>
</event>
<event type="pre-insert">
<listener class=
"org.hibernate.cfg.beanvalidation.BeanValidationEventListener"/>
</event>
<event type="pre-delete">
<listener class=
"org.hibernate.cfg.beanvalidation.BeanValidationEventListener"/>
</event>
</session-factory>
</hibernate-configuration>
10.1.3. JPA
If you are using JPA 2 and Hibernate Validator is in the classpath the JPA2 specification
requires that Bean Validation gets enabled. The properties
javax.persistence.validation.group.pre-persist,
javax.persistence.validation.group.pre-update and
javax.persistence.validation.group.pre-remove as described in Hibernate event-
based validation can in this case be configured in persistence.xml. persistence.xml also defines a
node validation-mode which can be set to AUTO, CALLBACK, NONE. The default is AUTO.
In a JPA 1 you will have to create and register Hibernate Validator yourself. In case you are using
Hibernate EntityManager you can add a customized version of the
BeanValidationEventListener described in Hibernate event-based validation to your
project and register it manually.
123
Validation within JSF2 shows an example of the f:validateBean tag in a JSF page. The
validationGroups attribute is optional and can be used to specify a comma separated list of
validation groups. The default is javax.validation.groups.Default. For more information
refer to the Seam documentation or the JSF 2 specification.
<h:form>
<f:validateBean validationGroups="javax.validation.groups.Default">
<h:inputText value=#{model.property}/>
<h:selectOneRadio value=#{model.radioProperty}> ...
</h:selectOneRadio>
<!-- other input components here -->
</f:validateBean>
</h:form>
javax.faces.validator.BeanValidator.MESSAGE={1}: {0}
javax.faces.validator.BeanValidator.MESSAGE={0}
10.3. CDI
As of version 1.1, Bean Validation is integrated with CDI (Contexts and Dependency Injection for
JavaTM EE).
This integration provides CDI managed beans for Validator and ValidatorFactory and
enables dependency injection in constraint validators as well as custom message interpolators,
traversable resolvers, constraint validator factories and parameter name providers.
124
Furthermore, parameter and return value constraints on the methods and constructors of CDI
managed beans will automatically be validated upon invocation.
When your application runs on a Java EE container, this integration is enabled by default. When
working with CDI in a Servlet container or in a pure Java SE environment, you can use the CDI
portable extension provided by Hibernate Validator. To do so, add the portable extension to
your class path as described in CDI.
CDI’s dependency injection mechanism makes it very easy to retrieve ValidatorFactory and
Validator instances and use them in your managed beans. Just annotate instance fields of
your bean with @javax.inject.Inject as shown in Retrieving validator factory and validator
via @Inject.
package org.hibernate.validator.referenceguide.chapter10.cdi.validator;
@ApplicationScoped
public class RentalStation {
@Inject
private ValidatorFactory validatorFactory;
@Inject
private Validator validator;
//...
}
The injected beans are the default validator factory and validator instances. In order to
configure them - e.g. to use a custom message interpolator - you can use the Bean Validation
XML descriptors as discussed in Configuring via XML.
If you are working with several Bean Validation providers you can make sure that factory and
validator from Hibernate Validator are injected by annotating the injection points with the
@HibernateValidator qualifier which is demonstrated in Using the @HibernateValidator
qualifier annotation.
125
Example 101. Using the @HibernateValidator qualifier annotation
package
org.hibernate.validator.referenceguide.chapter10.cdi.validator.qualifier;
@ApplicationScoped
public class RentalStation {
@Inject
@HibernateValidator
private ValidatorFactory validatorFactory;
@Inject
@HibernateValidator
private Validator validator;
//...
}
Via @Inject you also can inject dependencies into constraint validators and other Bean
Validation objects such as MessageInterpolator implementations etc.
Constraint validator with injected bean demonstrates how an injected CDI bean is used in a
ConstraintValidator implementation to determine whether the given constraint is valid or
not. As the example shows, you also can work with the @PostConstruct and @PreDestroy
callbacks to implement any required construction and destruction logic.
126
Example 102. Constraint validator with injected bean
package org.hibernate.validator.referenceguide.chapter10.cdi.injection;
@Inject
private VehicleRegistry vehicleRegistry;
@PostConstruct
public void postConstruct() {
//do initialization logic...
}
@PreDestroy
public void preDestroy() {
//do destruction logic...
}
@Override
public void initialize(ValidLicensePlate constraintAnnotation) {
}
@Override
public boolean isValid(String licensePlate,
ConstraintValidatorContext constraintContext) {
return vehicleRegistry.isValidLicensePlate( licensePlate );
}
}
The method interception facilities of CDI allow for a very tight integration with Bean Validation’s
method validation functionality. Just put constraint annotations to the parameters and return
values of the executables of your CDI beans and they will be validated automatically before
(parameter constraints) and after (return value constraints) a method or constructor is invoked.
Note that no explicit interceptor binding is required, instead the required method validation
interceptor will automatically be registered for all managed beans with constrained methods
and constructors.
The interceptor
org.hibernate.validator.internal.cdi.interceptor.Validatio
nInterceptor is registered by
org.hibernate.validator.internal.cdi.ValidationExtension.
This happens implicitly within a Java EE 7 runtime environment or explicitly
by adding the hibernate-validator-cdi artifact - see CDI
You can see an example in CDI managed beans with method-level constraints.
127
Example 103. CDI managed beans with method-level constraints
package
org.hibernate.validator.referenceguide.chapter10.cdi.methodvalidation;
@ApplicationScoped
public class RentalStation {
@Valid
public RentalStation() {
//...
}
@NotNull
@Valid
public Car rentCar(
@NotNull Customer customer,
@NotNull @Future Date startDate,
@Min(1) int durationInDays) {
//...
return null;
}
@NotNull
List<Car> getAvailableCars() {
//...
return null;
}
}
package
org.hibernate.validator.referenceguide.chapter10.cdi.methodvalidation;
@RequestScoped
public class RentCarRequest {
@Inject
private RentalStation rentalStation;
Here the RentalStation bean hosts several method constraints. When invoking one of the
RentalStation methods from another bean such as RentCarRequest, the constraints of the
invoked method are automatically validated. If any illegal parameter values are passed as in the
example, a ConstraintViolationException will be thrown by the method interceptor,
providing detailed information on the violated constraints. The same is the case if the method’s
return value violates any return value constraints.
128
Similarly, constructor constraints are validated automatically upon invocation. In the example
the RentalStation object returned by the constructor will be validated since the constructor
return value is marked with @Valid.
Bean Validation allows for a fine-grained control of the executable types which are
automatically validated. By default, constraints on constructors and non-getter methods are
validated. Therefore the @NotNull constraint on the method
RentalStation#getAvailableCars() in CDI managed beans with method-level constraints
gets not validated when the method is invoked.
You have the following options to configure which types of executables are validated upon
invocation:
• Configure the executable types globally via the XML descriptor META-INF/validation.xml;
see Configuring the validator factory in validation.xml for an example
129
Example 104. Using @ValidateOnExecution
package
org.hibernate.validator.referenceguide.chapter10.cdi.methodvalidation.con
figuration;
@ApplicationScoped
@ValidateOnExecution(type = ExecutableType.ALL)
public class RentalStation {
@Valid
public RentalStation() {
//...
}
@NotNull
@Valid
@ValidateOnExecution(type = ExecutableType.NONE)
public Car rentCar(
@NotNull Customer customer,
@NotNull @Future Date startDate,
@Min(1) int durationInDays) {
//...
return null;
}
@NotNull
public List<Car> getAvailableCars() {
//...
return null;
}
}
Here the method rentCar() won’t be validated upon invocation because it is annotated with
@ValidateOnExecution(type = ExecutableType.NONE). In contrast, the constructor and
the method getAvailableCars() will be validated due to @ValidateOnExecution(type =
ExecutableType.ALL) being given on the type level. ExecutableType.ALL is a more
compact form for explicitly specifying all the types CONSTRUCTORS, GETTER_METHODS and
NON_GETTER_METHODS.
Note that when a method overrides or implements a super-type method the configuration will
be taken from that overridden or implemented method (as given via @ValidateOnExecution
on the method itself or on the super-type). This protects a client of the super-type method from
an unexpected alteration of the configuration, e.g. disabling validation of an overridden
executable in a sub-type.
130
In case a CDI managed bean overrides or implements a super-type method and this super-type
method hosts any constraints, it can happen that the validation interceptor is not properly
registered with the bean, resulting in the bean’s methods not being validated upon invocation. In
this case you can specify the executable type IMPLICIT on the sub-class as shown in Using
ExecutableType.IMPLICIT, which makes sure that all required metadata is discovered an the
validation interceptor kicks in when the methods on ExpressRentalStation are invoked.
package
org.hibernate.validator.referenceguide.chapter10.cdi.methodvalidation.imp
licit;
@ValidateOnExecution(type = ExecutableType.ALL)
public interface RentalStation {
@NotNull
@Valid
Car rentCar(
@NotNull Customer customer,
@NotNull @Future Date startDate,
@Min(1) int durationInDays);
@NotNull
List<Car> getAvailableCars();
}
package
org.hibernate.validator.referenceguide.chapter10.cdi.methodvalidation.imp
licit;
@ApplicationScoped
@ValidateOnExecution(type = ExecutableType.IMPLICIT)
public class ExpressRentalStation implements RentalStation {
@Override
public Car rentCar(Customer customer, Date startDate, @Min(1) int
durationInDays) {
//...
return null;
}
@Override
public List<Car> getAvailableCars() {
//...
return null;
}
}
131
10.4. Java EE
When your application runs on a Java EE application server such as http://wildfly.org/, you also
can obtain Validator and ValidatorFactory instances via @Resource injection in managed
objects such as EJBs etc., as shown in Retrieving Validator and ValidatorFactory via
@Resource injection.
package org.hibernate.validator.referenceguide.chapter10.javaee;
@Resource
private ValidatorFactory validatorFactory;
@Resource
private Validator validator;
//...
}
Alternatively you can obtain a validator and a validator factory from JNDI under the names
"java:comp/Validator" and "java:comp/ValidatorFactory", respectively.
Similar to CDI-based injection via @Inject, these objects represent default validator and
validator factory and thus can be configured using the XML descriptor META-INF/validation.xml
(see Configuring via XML).
When your application is CDI-enabled, the injected objects are CDI-aware as well and e.g.
support dependency injection in constraint validators.
10.5. JavaFX
Hibernate Validator also provides support for the unwrapping of JavaFX properties. If JavaFX is
present on the classpath a ValidatedValueUnwrapper for JavaFX properties is automatically
registered. In some cases, however, it is also necessary to explicitly use
@UnwrapValidatedValue. This is required if the constraint validator resolution is not unique
and there is a potential constraint validator for the actual JavaFX property as well as the
contained property value itself. See JavaFX unwrapper for examples and further discussion.
132
Chapter 11. Hibernate Validator Specifics
In this chapter you will learn how to make use of several features provided by Hibernate
Validator in addition to the functionality defined by the Bean Validation specification. This
includes the fail fast mode, the API for programmatic constraint configuration and the boolean
composition of constraints.
org.hibernate.validator
Classes used by the Bean Validation bootstrap mechanism (eg. validation provider,
configuration class); for more details see Bootstrapping.
org.hibernate.validator.cfg, org.hibernate.validator.cfg.context,
org.hibernate.validator.cfg.defs, org.hibernate.validator.spi.cfg
org.hibernate.validator.constraints,
org.hibernate.validator.constraints.br,
org.hibernate.validator.constraints.pl
Some useful custom constraints provided by Hibernate Validator in addition to the built-in
constraints defined by the Bean Validation specification; the constraints are described in
detail in Additional constraints.
org.hibernate.validator.constraintvalidation
Extended constraint validator context which allows to set custom attributes for message
interpolation. HibernateConstraintValidatorContext describes how to make use of
that feature.
org.hibernate.validator.group, org.hibernate.validator.spi.group
133
The group sequence provider feature which allows you to define dynamic default group
sequences in function of the validated object state; the specifics can be found in Redefining
the default group sequence.
org.hibernate.validator.messageinterpolation,
org.hibernate.validator.resourceloading,
org.hibernate.validator.spi.resourceloading
Classes related to constraint message interpolation; the first package contains Hibernate
Validator’s default message interpolator, ResourceBundleMessageInterpolator. The
latter two packages provide the ResourceBundleLocator SPI for the loading of resource
bundles (see ResourceBundleLocator) and its default implementation.
org.hibernate.validator.parameternameprovider
A ParameterNameProvider based on the ParaNamer library, see ParaNamer based
ParameterNameProvider.
org.hibernate.validator.propertypath
Extensions to the javax.validation.Path API, see Extensions of the Path API.
org.hibernate.validator.spi.constraintdefinition
An SPI for registering additional constraint validators programmatically, see Providing
constraint definitions.
org.hibernate.validator.spi.time
An SPI for customizing the retrieval of the current time when validating @Future and
@Past, see Time providers for @Future and @Past.
org.hibernate.validator.valuehandling,
org.hibernate.validator.spi.valuehandling
Classes related to the processing of values prior to thei validation, see Unwrapping values.
The public packages of Hibernate Validator fall into two categories: while
the actual API parts are intended to be invoked or used by clients (e.g. the
Any packages not listed in that table are internal packages of Hibernate Validator and are not
intended to be accessed by clients. The contents of these internal packages can change from
release to release without notice, thus possibly breaking any client code relying on it.
134
11.2. Fail fast mode
Using the fail fast mode, Hibernate Validator allows to return from the current validation as soon
as the first constraint violation occurs. This can be useful for the validation of large object
graphs where you are only interested in a quick check whether there is any constraint violation
at all.
Using the fail fast validation mode shows how to bootstrap and use a fail fast enabled validator.
package org.hibernate.validator.referenceguide.chapter11.failfast;
@NotNull
private String manufacturer;
@AssertTrue
private boolean isRegistered;
assertEquals( 1, constraintViolations.size() );
Here the validated object actually fails to satisfy both the constraints declared on the Car class,
yet the validation call yields only one ConstraintViolation since the fail fast mode is
enabled.
135
There is no guarantee in which order the constraints are evaluated, i.e. it is
not deterministic whether the returned violation originates from the
Refer to Provider-specific settings to learn about the different ways of enabling the fail fast
mode when bootstrapping a validator.
As per specification a Bean Validation provider is allowed to relax these preconditions. With
Hibernate Validator you can do this in one of two ways.
136
Example 108. Configuring method validation behaviour in class hierarchies via properties
<property name=
"hibernate.validator.allow_parameter_constraint_override">true</property>
<property name=
"hibernate.validator.allow_multiple_cascaded_validation_on_result">true</
property>
<property name=
"hibernate.validator.allow_parallel_method_parameter_constraint">true</pr
operty>
</validation-config>
configuration.allowMultipleCascadedValidationOnReturnValues( true )
.allowOverridingMethodAlterParameterConstraint( true )
.allowParallelMethodsDefineParameterConstraints( true );
By default, all of these properties are false, implementing the default behavior as defined in the
Bean Validation specification.
Changing the default behaviour for method validation will result in non
specification conform and non portable application. Make sure to
understand what you are doing and that your use case really requires
changes to the default behaviour.
137
In addition, Hibernate Validator provides a fluent API which allows for the programmatic
configuration of constraints. Use cases include the dynamic addition of constraints at runtime
depending on some application state or tests where you need entities with different constraints
in different scenarios but don’t want to implement actual Java classes for each test case.
By default, constraints added via the fluent API are additive to constraints configured via the
standard configuration capabilities. But it is also possible to ignore annotation and XML
configured constraints where required.
The API is centered around the ConstraintMapping interface. You obtain a new mapping via
HibernateValidatorConfiguration#createConstraintMapping() which you then can
configure in a fluent manner as shown in Programmatic constraint declaration.
constraintMapping
.type( Car.class )
.property( "manufacturer", FIELD )
.constraint( new NotNullDef() )
.property( "licensePlate", FIELD )
.ignoreAnnotations()
.constraint( new NotNullDef() )
.constraint( new SizeDef().min( 2 ).max( 14 ) )
.type( RentalCar.class )
.property( "rentalStation", METHOD )
.constraint( new NotNullDef() );
Constraints can be configured on multiple classes and properties using method chaining. The
constraint definition classes NotNullDef and SizeDef are helper classes which allow to
configure constraint parameters in a type-safe fashion. Definition classes exist for all built-in
constraints in the org.hibernate.validator.cfg.defs package. By calling
ignoreAnnotations() any constraints configured via annotations or XML are ignored for the
given element.
Each element (type, property, method etc.) may only be configured once
within all the constraint mappings used to set up one validator factory.
Otherwise a ValidationException is raised.
138
It is not supported to add constraints to non-overridden supertype
Having configured the mapping, you must add it back to the configuration object from which you
then can obtain a validator factory.
For custom constraints you can either create your own definition classes extending
ConstraintDef or you can use GenericConstraintDef as seen in Programmatic declaration
of a custom constraint.
constraintMapping
.type( Car.class )
.property( "licensePlate", FIELD )
.constraint( new GenericConstraintDef<CheckCase>( CheckCase
.class )
.param( "value", CaseMode.UPPER )
);
By invoking valid() you can mark a member for cascaded validation which is equivalent to
annotating it with @Valid. Configure any group conversions to be applied during cascaded
validation using the convertGroup() method (equivalent to @ConvertGroup). An example
can be seen in Marking a property for cascaded validation.
constraintMapping
.type( Car.class )
.property( "driver", FIELD )
.constraint( new NotNullDef() )
.valid()
.convertGroup( Default.class ).to( PersonDefault.class )
.type( Person.class )
.property( "name", FIELD )
.constraint( new NotNullDef().groups( PersonDefault.class )
);
You can not only configure bean constraints using the fluent API but also method and
139
constructor constraints. As shown in Programmatic declaration of method and constructor
constraints constructors are identified by their parameter types and methods by their name and
parameter types. Having selected a method or constructor, you can mark its parameters and/or
return value for cascaded validation and add constraints as well as cross-parameter constraints.
constraintMapping
.type( Car.class )
.constructor( String.class )
.parameter( 0 )
.constraint( new SizeDef().min( 3 ).max( 50 ) )
.returnValue()
.valid()
.method( "drive", int.class )
.parameter( 0 )
.constraint( new MaxDef().value( 75 ) )
.method( "load", List.class, List.class )
.crossParameter()
.constraint( new GenericConstraintDef
<LuggageCountMatchesPassengerCount>(
LuggageCountMatchesPassengerCount.class ).param(
"piecesOfLuggagePerPassenger", 2
)
)
.method( "getDriver" )
.returnValue()
.constraint( new NotNullDef() )
.valid();
Last but not least you can configure the default group sequence or the default group sequence
provider of a type as shown in the following example.
Example 114. Configuration of default group sequence and default group sequence provider
constraintMapping
.type( Car.class )
.defaultGroupSequence( Car.class, CarChecks.class )
.type( RentalCar.class )
.defaultGroupSequenceProviderClass(
RentalCarGroupSequenceProvider.class );
140
11.5. Applying programmatic constraint declarations
to the default validator factory
If you are not bootstrapping a validator factory manually but work with the default factory as
configured via META-INF/validation.xml (see Configuring via XML), you can add one or more
constraint mappings by creating one or several constraint mapping contributors. To do so,
implement the ConstraintMappingContributor contract:
package org.hibernate.validator.referenceguide.chapter11.constraintapi;
@Override
public void createConstraintMappings(ConstraintMappingBuilder
builder) {
builder.addConstraintMapping()
.type( Marathon.class )
.property( "name", METHOD )
.constraint( new NotNullDef() )
.property( "numberOfHelpers", FIELD )
.constraint( new MinDef().value( 1 ) );
builder.addConstraintMapping()
.type( Runner.class )
.property( "paidEntryFee", FIELD )
.constraint( new AssertTrueDef() );
}
}
You then need to specify the fully-qualified class name of the contributor implementation in
META-INF/validation.xml, using the property key
hibernate.validator.constraint_mapping_contributors. You can specify several
contributors by separating them with a comma.
In case you specify a purely composed constraint - i.e. a constraint which has no validator itself
but is solely made up from other, composing constraints - on a method declaration, the
validation engine cannot determine whether that constraint is to be applied as a return value
constraint or as a cross-parameter constraint.
141
@SupportedValidationTarget annotation on the declaration of the composed constraint
type as shown in Specifying the validation target of a purely composed constraint. The
@ValidInvoiceAmount does not declare any validator, but it is solely composed by the @Min
and @NotNull constraints. The @SupportedValidationTarget ensures that the constraint is
applied to the method return value when given on a method declaration.
package org.hibernate.validator.referenceguide.chapter11.purelycomposed;
@Min(value = 0)
@NotNull
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {})
@SupportedValidationTarget(ValidationTarget.ANNOTATED_ELEMENT)
@ReportAsSingleViolation
public @interface ValidInvoiceAmount {
Bean Validation specifies that the constraints of a composed constraint (see Constraint
composition) are all combined via a logical AND. This means all of the composing constraints
need to return true in order for an overall successful validation.
Hibernate Validator offers an extension to this and allows you to compose constraints via a
logical OR or NOT. To do so you have to use the ConstraintComposition annotation and the
enum CompositionType with its values AND, OR and ALL_FALSE.
142
Example 117. OR composition of constraints
package
org.hibernate.validator.referenceguide.chapter11.booleancomposition;
@ConstraintComposition(OR)
@Pattern(regexp = "[a-z]")
@Size(min = 2, max = 3)
@ReportAsSingleViolation
@Target({ METHOD, FIELD })
@Retention(RUNTIME)
@Constraint(validatedBy = { })
public @interface PatternOrSize {
String message() default
"{org.hibernate.validator.referenceguide.chapter11." +
"booleancomposition.PatternOrSize.message}";
143
Example 118. Getting the value from property nodes
node = nodeIterator.next();
assertEquals( node.getName(), "resident" );
assertSame( node.as( PropertyNode.class ).getValue(), bob );
node = nodeIterator.next();
assertEquals( node.getName(), "name" );
assertEquals( node.as( PropertyNode.class ).getValue(), "Bob" );
This is specifically useful to obtain the element of Set properties on the property path (e.g.
apartments in the example) which otherwise could not be identified (unlike for Map and List,
there is no key nor index in this case).
144
Example 119. ConstraintValidator implementation setting a dynamic payload
package org.hibernate.validator.referenceguide.chapter11.dynamicpayload;
import static
org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
static {
suggestedCars.put( 2, "Chevrolet Corvette" );
suggestedCars.put( 3, "Toyota Volta" );
suggestedCars.put( 4, "Maserati GranCabrio" );
suggestedCars.put( 5, " Mercedes-Benz E-Class" );
}
@Override
public void initialize(ValidPassengerCount constraintAnnotation) {
}
@Override
public boolean isValid(Car car, ConstraintValidatorContext context) {
if ( car == null ) {
return true;
}
if ( suggestedCars.containsKey( passengerCount ) ) {
HibernateConstraintValidatorContext hibernateContext =
context.unwrap(
HibernateConstraintValidatorContext.class
);
hibernateContext.withDynamicPayload( suggestedCars.get(
passengerCount ) );
}
return false;
}
}
}
145
Example 120. Retrieval of a ConstraintViolations’s dynamic payload
assertEquals( 1, constraintViolations.size() );
11.9. ParameterMessageInterpolator
Hibernate Validator requires per default an implementation of the Unified EL (see Unified EL) to
be available. This is needed to allow the interpolation of constraint error messages using EL
expressions as defined by Bean Validation 1.1.
For environments where you cannot or do not want to provide an EL implementation, Hibernate
Validators offers a non EL based message interpolator -
org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator
.
Refer to Custom message interpolation to see how to plug in custom message interpolator
implementations.
11.10. ResourceBundleLocator
With ResourceBundleLocator, Hibernate Validator provides an additional SPI which allows to
146
retrieve error messages from other resource bundles than ValidationMessages while still using
the actual interpolation algorithm as defined by the specification. Refer to
ResourceBundleLocator to learn how to make use of that SPI.
11.11.1. HibernateConstraintValidatorContext
• set arbitrary parameters for interpolation via the Expression Language message
interpolation facility using
HibernateConstraintValidatorContext#addExpressionVariable(String,
Object). For an example refer to Custom @Future validator with message parameters.
includes the default constraint violation, but also all violations created
by the ConstraintViolationBuilder. You can, however, update the
parameters between invocations of
ConstraintViolationBuilder#addConstraintViolation().
• obtain the TimeProvider for getting the current time when validating @Future and @Past
constraints (see also Time providers for @Future and @Past).
This is useful if you want to customize the message of the @Future constraint. By default
the message is just "must be in the future". Custom @Future validator with message
parameters shows how to include the current date in order to make the message more
explicit.
147
Example 121. Custom @Future validator with message parameters
package org.hibernate.validator.referenceguide.chapter11.context;
@Override
public void initialize(Future constraintAnnotation) {
}
@Override
public boolean isValid(Date value, ConstraintValidatorContext
context) {
if ( value == null ) {
return true;
}
HibernateConstraintValidatorContext hibernateContext =
context.unwrap(
HibernateConstraintValidatorContext.class
);
if ( !value.after( now ) ) {
hibernateContext.disableDefaultConstraintViolation();
hibernateContext.addExpressionVariable( "now", now )
.buildConstraintViolationWithTemplate( "Must be
after ${now}" )
.addConstraintViolation();
return false;
}
return true;
}
}
11.11.2. HibernateMessageInterpolatorContext
148
usecases, let us know.
/**
* Returns the currently validated root bean type.
*
* @return The currently validated root bean type.
This library provides several ways for obtaining parameter names at runtime, e.g. based on
debug symbols created by the Java compiler, constants with the parameter names woven into
the bytecode in a post-compile step or annotations such as the @Named annotation from JSR
330.
When using this parameter name provider, you need to add the ParaNamer
library to your classpath. It is available in the Maven Central repository with
the group id com.thoughtworks.paranamer and the artifact id
paranamer.
149
constraint to wrapped value of a JavaFX property a JavaFX property type is used to define an
element of a domain model. The @Size constraint is meant to be applied to the string value not
the wrapping Property instance.
@Size(min = 3)
private Property<String> name = new SimpleStringProperty( "Bob" );
and may evolve into more general means of value handling in future
releases. Please let us know about your use cases for such functionality.
Bean properties in JavaFX are typically not of simple data types like String or int, but are
wrapped in Property types which allows to make them observable, use them for data binding
etc. When applying a constraint such as @Size to an element of type Property<String>
without further preparation, an exception would be raised, indicating that no suitable validator
for that constraint and data type can be found. Thus the validated value must be unwrapped
from the containing property object before looking up a validator and invoking it.
package org.hibernate.validator.referenceguide.chapter11.valuehandling;
@Override
public Object handleValidatedValue(Property<?> value) {
//...
return null;
}
@Override
public Type getValidatedValueType(Type valueType) {
//...
return null;
}
}
150
Example 125. Registering a ValidatedValueUnwrapper
Hibernate Validator provides built-in unwrapping for Optional introduced in Java 8. The
unwrapper is registered automatically in Java 8 environments, and no further configuration is
required. An example of unwrapping an Optional instance is shown in Unwrapping Optional
instances.
@Size(min = 3)
private Optional<String> firstName = Optional.of( "John" );
@NotNull
@UnwrapValidatedValue // UnwrapValidatedValue required since otherwise
unclear which value to validate
private Optional<String> lastName = Optional.of( "Doe" );
151
Optional.empty() is treated as null during validation. This means that
Hibernate Validator also provides built-in unwrapping for JavaFX property values. The
unwrapper is registered automatically for environments where JavaFX is present, and no further
configuration is required. ObservableValue and its sub-types are supported. An example of
some of the different ways in which JavaFX property values can be unwrapped is shown in
Unwrapping JavaFX properties.
@Min(value = 3)
IntegerProperty integerProperty1 = new SimpleIntegerProperty( 4 );
@Min(value = 3)
Property<Number> integerProperty2 = new SimpleIntegerProperty( 4 );
@Min(value = 3)
ObservableValue<Number> integerProperty3 = new SimpleIntegerProperty( 4
);
Unwrapping can also be used with object graphs (cascaded validation) as shown in Unwrapping
Optional prior to cascaded validation via @Valid. When validating the object holding the
Optional<Person>, a cascaded validation of the Person object would be performed.
@Valid
private Optional<Person> person = Optional.of( new Person() );
@Size(min = 3)
private String name = "Bob";
152
11.14. Providing constraint definitions
Bean Validation allows to (re-)define constraint definitions via XML in its constraint mapping
files. See Mapping constraints via constraint-mappings for more information and Bean
constraints configured via XML for an example. While this approach is sufficient for many use
cases, it has it shortcomings in others. Imagine for example a constraint library wanting to
contribute constraint definitions for custom types. This library could provide a mapping file with
their library, but this file still would need to be referenced by the user of the library. Luckily
there are better ways.
This mechanism is also helpful when creating large multi-module applications: Instead of putting
all the constraint messages into one single bundle, you can have one resource bundle per
module containing only those messages of that module.
While the service loader approach works in many scenarios, but not in all (think for example
OSGi where service files are not visible), there is yet another way of contributing constraint
definitions. You can use the programmatic constraint declaration API - see Adding constraint
definitions through the programmatic API.
153
Example 130. Adding constraint definitions through the programmatic API
constraintMapping
.constraintDefinition( ValidPassengerCount.class )
.validatedBy( ValidPassengerCountValidator.class );
Instead of directly adding a constraint mapping to the configuration object, you may use a
ConstraintMappingContributor as detailed in Applying programmatic constraint
declarations to the default validator factory. This can be useful when configuring the default
validator factory using META-INF/validation.xml (see Configuring via XML).
constraintMapping
.constraintDefinition( URL.class )
.includeExistingValidators( false )
.validatedBy( RegexpURLValidator.class );
• classes specified by name in XML descriptors (e.g. custom message interpolators etc.)
154
By default Hibernate Validator tries to load these resources via the current thread context
classloader. If that’s not successful, Hibernate Validator’s own classloader will be tried as a
fallback.
For cases where this strategy is not appropriate (e.g. modularized environments such as OSGi),
you may provide a specific classloader for loading these resources when bootstrapping the
validator factory:
Example 131. Providing a classloader for loading external resources and classes
In the case of OSGi, you could e.g. pass the loader of a class from the bundle bootstrapping
Hibernate Validator or a custom classloader implementation which delegates to
Bundle#loadClass() etc.
To address such scenarios, Hibernate Validator provides a custom contract for obtaining the
current time, TimeProvider. Using a custom TimeProvider shows an implementation of this
contract and its registration when bootstrapping a validator factory.
155
Example 132. Using a custom TimeProvider
package org.hibernate.validator.referenceguide.chapter11.timeprovider;
@Override
public long getCurrentTime() {
Calendar now = GregorianCalendar.getInstance();
return now.getTimeInMillis();
}
}
156
Chapter 12. Annotation Processor
Have you ever caught yourself by unintentionally doing things like
Then the Hibernate Validator Annotation Processor is the right thing for you. It helps preventing
such mistakes by plugging into the build process and raising compilation errors whenever
constraint annotations are incorrectly used.
You can find the Hibernate Validator Annotation Processor as part of the
distribution bundle on Sourceforge or in the usual Maven repositories such
as Maven Central under the GAV org.hibernate:hibernate-
validator-annotation-processor:5.4.2.Final.
12.1. Prerequisites
The Hibernate Validator Annotation Processor is based on the "Pluggable Annotation
Processing API" as defined by JSR 269 which is part of the Java Platform since Java 6.
12.2. Features
As of Hibernate Validator 5.4.2.Final the Hibernate Validator Annotation Processor checks that:
• constraint annotations are allowed for the type of the annotated element
• only such methods are annotated with constraint annotations which are valid JavaBeans
getter methods (optionally, see below)
• only such annotation types are annotated with constraint annotations which are constraint
annotations themselves
• method return value constraints in inheritance hierarchies respect the inheritance rules
157
12.3. Options
The behavior of the Hibernate Validator Annotation Processor can be controlled using the
following processor options:
diagnosticKind
Controls how constraint problems are reported. Must be the string representation of one of
the values from the enum javax.tools.Diagnostic.Kind, e.g. WARNING. A value of
ERROR will cause compilation to halt whenever the AP detects a constraint problem. Defaults
to ERROR.
methodConstraintsSupported
Controls whether constraints are allowed at methods of any kind. Must be set to true when
working with method level constraints as supported by Hibernate Validator. Can be set to
false to allow constraints only at JavaBeans getter methods as defined by the Bean
Validation API. Defaults to true.
verbose
Controls whether detailed processing information shall be displayed or not, useful for
debugging purposes. Must be either true or false. Defaults to false.
javac
When compiling on the command line using javac, specify the JAR hibernate-validator-
annotation-processor-5.4.2.Final.jar using the "processorpath" option as shown in the following
listing. The processor will be detected automatically by the compiler and invoked during
compilation.
javac src/main/java/org/hibernate/validator/ap/demo/Car.java \
-cp /path/to/validation-api-1.1.0.Final.jar \
-processorpath /path/to/hibernate-validator-annotation-processor-
5.4.2.Final.jar
158
Apache Ant
Similar to directly working with javac, the annotation processor can be added as as compiler
argument when invoking the javac task for Apache Ant:
<javac srcdir="src/main"
destdir="build/classes"
classpath="/path/to/validation-api-1.1.0.Final.jar">
<compilerarg value="-processorpath" />
<compilerarg value="/path/to/hibernate-validator-annotation-
processor-5.4.2.Final.jar"/>
</javac>
Maven
For using the Hibernate Validator annotation processor with Maven, set it up via the
annotationProcessorPaths option like this:
159
Example 135. Using the HV Annotation Processor with Maven
<project>
[...]
<build>
[...]
<plugins>
[...]
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator-annotation-
processor</artifactId>
<version>5.4.2.Final</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
[...]
</plugins>
[...]
</build>
[...]
</project>
Eclipse
The annotation processor will automatically be set up for Maven projects configured as
described above, provided you have the M2E Eclipse plug-in installed.
For plain Eclipse projects follow these steps to set up the annotation processor:
• Go to "Java Compiler" and make sure, that "Compiler compliance level" is set to "1.6".
Otherwise the processor won’t be activated
• Go to "Java Compiler - Annotation Processing - Factory Path" and add the JAR hibernate-
validator-annotation-processor-5.4.2.Final.jar
160
You now should see any annotation problems as regular error markers within the editor and in
the "Problem" view:
IntelliJ IDEA
The following steps must be followed to use the annotation processor within IntelliJ IDEA
(version 9 and above):
• Choose "Enable annotation processing" and enter the following as "Processor path":
/path/to/hibernate-validator-annotation-processor-5.4.2.Final.jar
Rebuilding your project then should show any erroneous constraint annotations:
161
NetBeans
Starting with version 6.9, also the NetBeans IDE supports using annotation processors within
the IDE build. To do so, do the following:
Any constraint annotation problems will then be marked directly within the editor:
162
12.5. Known issues
The following known issues exist as of May 2010:
• HV-308: Additional validators registered for a constraint using XML are not evaluated by
the annotation processor.
• Sometimes custom constraints can’t be properly evaluated when using the processor within
Eclipse. Cleaning the project can help in these situations. This seems to be an issue with the
Eclipse JSR 269 API implementation, but further investigation is required here.
• When using the processor within Eclipse, the check of dynamic default group sequence
definitions doesn’t work. After further investigation, it seems to be an issue with the Eclipse
JSR 269 API implementation.
163
Chapter 13. Further reading
Last but not least, a few pointers to further information.
A great source for examples is the Bean Validation TCK which is available for anonymous access
on GitHub. In particular the TCK’s tests might be of interest. The JSR 349 specification itself is
also a great way to deepen your understanding of Bean Validation resp. Hibernate Validator.
If you have any further questions to Hibernate Validator or want to share some of your use cases
have a look at the Hibernate Validator Wiki and the Hibernate Validator Forum.
In case you would like to report a bug use Hibernate’s Jira instance. Feedback is always
welcome!
164