Hibernate.orgCommunity Documentation
In the following sections we are having a closer look at some of the Hibernate Validator specific features (features which are not part of the Bean Validation specification). This includes the fail fast mode, the programmatic constraint configuration API and boolean composition of composing constraints.
The features described in the following sections are not portable between Bean Validation providers/implementations.
Let's start, however, with a look at the public API of Hibernate Validator. Table 8.1, “Hibernate Validator public API” lists all packages belonging to this API and describes their purpose.
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.
In the following table, when a package is public its not necessarily true for its nested packages.
Table 8.1. Hibernate Validator public API
Packages | Description |
---|---|
org.hibernate.validator | This package contains the classes used by the Bean Validation bootstrap mechanism (eg. validation provider, configuration class). For more details see Chapter 5, Bootstrapping. |
org.hibernate.validator.cfg, org.hibernate.validator.cfg.context, org.hibernate.validator.cfg.defs | With Hibernate Validator you can define constraints via a
fluent API. These packages contain all classes needed to use this
feature. In the package
org.hibernate.validator.cfg you will find the
ConstraintMapping class and in package
org.hibernate.validator.cfg.defs all constraint
definitions. For more details see Section 8.4, “Programmatic constraint definition”. |
org.hibernate.validator.constraints | In addition to Bean Validation constraints, Hibernate Validator provides some useful custom constraints. This package contains all custom annotation classes. For more details see Section 2.4.2, “Additional constraints”. |
org.hibernate.validator.group | With Hibernate Validator you can define dynamic default
group sequences in function of the validated object state. This
package contains all classes needed to use this feature
(GroupSequenceProvider annotation and
DefaultGroupSequenceProvider contract). For
more details see Section 2.3.2, “Redefining the default group sequence of a class”. |
org.hibernate.validator.messageinterpolation, org.hibernate.validator.resourceloading | These packages contain the classes related to constraint
message interpolation. The first package contains two
implementations of MessageInterpolator . The
first one,
ValueFormatterMessageInterpolator allows to
interpolate the validated value into the constraint message, see
Section 5.3, “MessageInterpolator”. The second
implementation named
ResourceBundleMessageInterpolator is the
implementation used by default by Hibernate Validator. This
implementation relies on a
ResourceBundleLocator , see Section 5.3.1, “ResourceBundleLocator”. Hibernate Validator
provides different ResourceBundleLocator
implementations located in the package
org.hibernate.validator.resourceloading. |
org.hibernate.validator.method, org.hibernate.validator.method.metadata | Hibernate Validator provides support for method-level
constraints based on appendix C of the Bean Validation
specification. The first package contains the
MethodValidator interface allowing you to
validate method return values and parameters. The second package
contains meta data for constraints hosted on parameters and
methods which can be retrieved via the
MethodValidator . |
First off, the fail fast mode. Hibernate Validator allows to return from the current validation as soon as the first constraint violation occurs. This is called the fail fast mode and can be useful for validation of large object graphs where one is only interested whether there is a constraint violation or not. Example 8.1, “Enabling failFast via a property”, Example 8.2, “Enabling failFast at the Configuration level” and Example 8.3, “Enabling failFast at the ValidatorFactory level” show multiple ways to enable the fail fast mode.
Example 8.1. Enabling failFast
via a property
HibernateValidatorConfiguration configuration = Validation.byProvider( HibernateValidator.class ).configure(); ValidatorFactory factory = configuration.addProperty( "hibernate.validator.fail_fast", "true" ).buildValidatorFactory(); Validator validator = factory.getValidator(); // do some actual fail fast validation ...
Example 8.2. Enabling failFast
at the
Configuration
level
HibernateValidatorConfiguration configuration = Validation.byProvider( HibernateValidator.class ).configure(); ValidatorFactory factory = configuration.failFast( true ).buildValidatorFactory(); Validator validator = factory.getValidator(); // do some actual fail fast validation ...
Example 8.3. Enabling failFast
at the
ValidatorFactory
level
HibernateValidatorConfiguration configuration = Validation.byProvider( HibernateValidator.class ).configure(); ValidatorFactory factory = configuration.buildValidatorFactory(); Validator validator = factory.getValidator(); // do some non fail fast validation ... validator = factory.unwrap( HibernateValidatorFactory.class ) .usingContext() .failFast( true ) .getValidator(); // do fail fast validation ...
The Bean Validation API allows to specify constraints for fields, properties and types. Hibernate Validator goes one step further and allows to place contraint annotations also on method parameters and method return values, thus enabling a programming style known as "Programming by Contract".
More specifically this means that Bean Validation constraints can be used to specify
the preconditions that must be met before a method invocation (by annotating method parameters with constraints) and
the postconditions that are guaranteed after a method invocation (by annotating methods)
This approach has several advantages over traditional ways of parameter and return value checking:
The checks don't have to be performed manually (e.g. by throwing
IllegalArgumentExceptions
or similar),
resulting in less code to write and maintain.
A method's pre- and postconditions don't have to be expressed again in the method's JavaDoc, since the constraint annotations will automatically be included in the generated JavaDoc. This avoids redundancy and reduces the chance of inconsistencies between implementation and documentation.
Method validation was also considered to be included in the Bean Validation API as defined by JSR 303, but it didn't become part of the 1.0 version. A basic draft is outlined in appendix C of the specification, and the implementation in Hibernate Validator is largely influenced by this draft. The feature is considered again for inclusion in BV 1.1.
Example 8.4, “Using method-level constraints” demonstrates the definition of method-level constraints.
Example 8.4. Using method-level constraints
public class RentalStation { @NotNull public Car rentCar(@NotNull Customer customer, @NotNull @Future Date startDate, @Min(1) int durationInDays) { //... } }
Here the following pre- and postconditions for the
rentCar()
method are declared:
The renting customer may not be null
The rental's start date must not be null and must be in the future
The rental duration must be at least one day
The returned Car
instance may not be
null
Using the @Valid
annotation it's also
possible to define that a cascaded validation of parameter or return
value objects shall be performed. An example can be found in Example 8.5, “Cascaded validation of method-level constraints”.
Example 8.5. Cascaded validation of method-level constraints
public class RentalStation { @Valid public Set<Rental> getRentalsByCustomer(@Valid Customer customer) { //... } }
Here all the constraints declared at the
Customer
type will be evaluated when validating
the method parameter and all constraints declared at the returned
Rental
objects will be evaluated when validating
the method's return value.
Special care must be taken when defining parameter constraints in inheritance hierarchies.
When a method is overridden in sub-types method parameter constraints can only be declared at the base type. The reason for this restriction is that the preconditions to be fulfilled by a type's client must not be strengthened in sub-types (which may not even be known to the base type's client). Note that also if the base method doesn't declare any parameter constraints at all, no parameter constraints may be added in overriding methods.
The same restriction applies to interface methods: no parameter constraints may be defined at the implementing method (or the same method declared in sub-interfaces).
If a violation of this rule is detected by the validation
engine, a
javax.validation.ConstraintDeclarationException
will be thrown. In Example 8.6, “Illegal parameter constraint declarations” some examples for
illegal parameter constraints declarations are shown.
Example 8.6. Illegal parameter constraint declarations
public class Car { public void drive(Person driver) { ... } } public class RentalCar extends Car { //not allowed, parameter constraint added in overriding method public void drive(@NotNull Person driver) { ... } } public interface ICar { void drive(Person driver); } public class CarImpl implements ICar { //not allowed, parameter constraint added in implementation of interface method public void drive(@NotNull Person driver) { ... } }
This rule only applies to parameter constraints, return value constraints may be added in sub-types without any restrictions as it is alright to strengthen the postconditions guaranteed to a type's client.
To validate method-level constraints Hibernate Validator provides
the interface
org.hibernate.validator.method.MethodValidator
.
As shown in Example 8.7, “The MethodValidator interface” this interface defines methods for the evaluation of parameter as well as return value constraints and for retrieving an extended type descriptor providing method constraint related meta data.
Example 8.7. The MethodValidator
interface
public interface MethodValidator { <T> Set<MethodConstraintViolation<T>> validateParameter(T object, Method method, Object parameterValue, int parameterIndex, Class<?>... groups); <T> Set<MethodConstraintViolation<T>> validateAllParameters(T object, Method method, Object[] parameterValues, Class<?>... groups); <T> Set<MethodConstraintViolation<T>> validateReturnValue(T object, Method method, Object returnValue, Class<?>... groups); TypeDescriptor getConstraintsForType(Class<?> clazz); }
To retrieve a method validator get hold of an instance of HV's
javax.validation.Validator
implementation and
unwrap it to MethodValidator
as shown in Example 8.8, “Retrieving a MethodValidator
instance”.
Example 8.8. Retrieving a MethodValidator
instance
MethodValidator methodValidator = Validation.byProvider( HibernateValidator.class ) .configure() .buildValidatorFactory() .getValidator() .unwrap( MethodValidator.class );
The validation methods defined on
MethodValidator
each return a
Set<MethodConstraintViolation>
. The type
MethodConstraintViolation
(see Example 8.9, “The MethodConstraintViolation
type”) extends
javax.validation.ConstraintViolation
and provides
additional method level validation specific information such as the
method and index of the parameter which caused the constraint
violation.
Example 8.9. The MethodConstraintViolation
type
public interface MethodConstraintViolation<T> extends ConstraintViolation<T> { public static enum Kind { PARAMETER, RETURN_VALUE } Method getMethod(); Integer getParameterIndex(); String getParameterName(); Kind getKind(); }
The method getParameterName()
currently
returns synthetic parameter identifiers such as "arg0", "arg1" etc. In
a future version of Hibernate Validator support for specifying
parameter identifiers might be added.
Typically the validation of method-level constraints is not
invoked manually but automatically upon method invocation by an
integration layer using AOP (aspect-oriented programming) or similar
method interception facilities such as the JDK's
java.lang.reflect.Proxy
API or CDI ("JSR 299:
Contexts and Dependency Injection for the
JavaTM EE platform").
If a parameter or return value constraint can't be validated
sucessfully such an integration layer typically will throw a
MethodConstraintViolationException
which similar
to javax.validation.ConstraintViolationException
contains a set with the occurred constraint violations.
If you are using CDI you might be interested in the Seam Validation project. This Seam module provides an interceptor which integrates the method validation functionality with CDI.
As outlined in Chapter 6, Metadata API the Bean Validation API provides rich capabilities for retrieving constraint related meta data. Hibernate Validator extends this API and allows to retrieve constraint meta data also for method-level constraints.
Example 8.10, “Retrieving meta data for method-level constraints” shows
how to use this extended API to retrieve constraint meta data for the
rentCar()
method from the
RentalStation
type.
Example 8.10. Retrieving meta data for method-level constraints
TypeDescriptor typeDescriptor = methodValidator.getConstraintsForType(RentalStation.class) //retrieve a descriptor for the rentCar() method MethodDescriptor rentCarMethod = typeDescriptor.getConstraintsForMethod("rentCar", Customer.class, Date.class, int.class); assertEquals(rentCarMethod.getMethodName(), "rentCar"); assertTrue(rentCarMethod.hasConstraints()); assertFalse(rentCarMethod.isCascaded()); //retrieve constraints from the return value Set<ConstraintDescriptor<?>> returnValueConstraints = rentCarMethod.findConstraints().getConstraintDescriptors(); assertEquals(returnValueConstraints.size(), 1); assertEquals(returnValueConstraints.iterator().next().getAnnotation().annotationType(), NotNull.class); List<ParameterDescriptor> allParameters = rentCarMethod.getParameterDescriptors(); assertEquals(allParameters.size(), 3); //retrieve a descriptor for the startDate parameter ParameterDescriptor startDateParameter = allParameters.get(1); assertEquals(startDateParameter.getIndex(), 1); assertFalse(startDateParameter.isCascaded()); assertEquals(startDateParameter.findConstraints().getConstraintDescriptors().size(), 2);
Refer to the JavaDoc
of the package
org.hibernate.validator.method.metadata
for more
details on the extended meta data API.
Another addition to the Bean Validation specification is the ability to configure constraints via a fluent API. This API can be used exclusively or in combination with annotations and xml. If used in combination programmatic constraints are additive to constraints configured via the standard configuration capabilities.
The API is centered around the
ConstraintMapping
class which can be found in the
package org.hibernate.validator.cfg. Starting with the
instantiation of a new ConstraintMapping
,
constraints can be defined in a fluent manner as shown in Example 8.11, “Programmatic constraint definition”.
Example 8.11. Programmatic constraint definition
ConstraintMapping mapping = new ConstraintMapping(); mapping.type( Car.class ) .property( "manufacturer", FIELD ) .constraint( new NotNullDef() ) .property( "licensePlate", FIELD ) .constraint( new NotNullDef() ) .constraint( new SizeDef().min( 2 ).max( 14 ) ) .property( "seatCount", FIELD ) .constraint( new MinDef()value ( 2 ) ) .type( RentalCar.class ) .property( "rentalStation", METHOD ) .constraint( new NotNullDef() );
As you can see constraints can be configured on multiple classes and
properties using method chaining. The constraint definition classes
NotNullDef
, SizeDef
and
MinDef
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.
For custom constraints you can either create your own definition
classes extending ConstraintDef
or you can use
GenericConstraintDef
as seen in Example 8.12, “Programmatic constraint definition using
createGeneric()”.
Example 8.12. Programmatic constraint definition using
createGeneric()
ConstraintMapping mapping = new ConstraintMapping(); mapping.type( Car.class ) .property( "licensePlate", FIELD ) .constraint( new GenericConstraintDef<CheckCase.class>( CheckCase.class ).param( "value", CaseMode.UPPER ) );
Not only standard class- and property-level constraints but also method constraints can be configured using the API. As shown in Example 8.13, “Programmatic definition of method constraints” methods are identified by their name and their parameters (if there are any). Having selected a method, constraints can be placed on the method's parameters and/or return value.
Example 8.13. Programmatic definition of method constraints
ConstraintMapping mapping = new ConstraintMapping(); mapping.type( Car.class ) .method( "drive", String.class, Integer.class ) .parameter( 0 ) .constraint( new NotNullDef() ) .constraint( new MinDef().value ( 1 ) ) .parameter( 1 ) .constraint( new NotNullDef() ) .returnValue() .constraint( new NotNullDef() ) .method( "check" ) .returnValue() .constraint( new NotNullDef() );
Using the API it's also possible to mark properties, method
parameters and method return values as cascading (equivalent to annotating
them with @Valid
). An example can be found in Example 8.14, “Marking constraints for cascaded validation”.
Example 8.14. Marking constraints for cascaded validation
ConstraintMapping mapping = new ConstraintMapping(); mapping.type( Car.class ) .property( "manufacturer", FIELD ) .valid() .property( "licensePlate", METHOD ) .valid() .method( "drive", String.class, Integer.class ) .parameter( 0 ) .valid() .parameter( 1 ) .valid() .returnValue() .valid() .type( RentalCar.class ) .property( "rentalStation", METHOD ) .valid();
Last but not least you can configure the default group sequence or the default group sequence provider of a type as shown in Example 8.15, “Configuration of default group sequence and default group sequence provider”.
Example 8.15. Configuration of default group sequence and default group sequence provider
ConstraintMapping mapping = new ConstraintMapping(); mapping.type( Car.class ) .defaultGroupSequence( Car.class, CarChecks.class ) .type( RentalCar.class ) .defaultGroupSequenceProvider( RentalCarGroupSequenceProvider.class );
Once a ConstraintMapping
is set up it has to
be passed to the configuration. Since the programmatic API is not part of
the official Bean Validation specification you need to get hold of a
HibernateValidatorConfiguration
instance as shown
in Example 8.16, “Creating a Hibernate Validator specific configuration”.
Example 8.16. Creating a Hibernate Validator specific configuration
ConstraintMapping mapping = new ConstraintMapping(); // configure mapping instance HibernateValidatorConfiguration config = Validation.byProvider( HibernateValidator.class ).configure(); config.addMapping( mapping ); ValidatorFactory factory = config.buildValidatorFactory(); Validator validator = factory.getValidator();
As per Bean Validation specification the constraints of a composed
constraint (see Section 3.2, “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 logical AND combination which
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. Example 8.17, “OR composition of constraints” shows how to build a
composing constraint where only one of the constraints has to be
successful in order to pass the validation. Either the validated string is
all lowercased or it is between two and three characters long.
Example 8.17. OR composition of constraints
@ConstraintComposition(OR)
@Pattern(regexp = "[a-z]")
@Size(min = 2, max = 3)
@ReportAsSingleViolation
@Target({ METHOD, FIELD })
@Retention(RUNTIME)
@Constraint(validatedBy = { })
public @interface PatternOrSize {
public abstract String message() default "{PatternOrSize.message}";
public abstract Class<?>[] groups() default { };
public abstract Class<? extends Payload>[] payload() default { };
}
Using ALL_FALSE as composition type implicitly enforces that only a single violation will get reported in case validation of the constraint composition fails.
Copyright © 2009 - 2011 Red Hat, Inc. & Gunnar Morling