Hibernate.orgCommunity Documentation

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.

Note

Using the features described in the following sections may result in application code which is not portable between Bean Validation providers.

11.1. Public API

Let’s start, however, with a look at the public API of Hibernate Validator. Table 11.1, “Hibernate Validator public API” lists all packages belonging to this API and describes their purpose. Note that when a package is part of the public this is not necessarily true for its sub-packages.

Table 11.1. Hibernate Validator public API
PackagesDescription

org.hibernate.validator

Classes used by the Bean Validation bootstrap mechanism (eg. validation provider, configuration class); For more details see Chapter 8, Bootstrapping.

org.hibernate.validator.cfg, org.hibernate.validator.cfg.context, org.hibernate.validator.cfg.defs, org.hibernate.validator.spi.cfg

Hibernate Validator’s fluent API for constraint declaration; In org.hibernate.validator.cfg you will find the ConstraintMapping interface, in org.hibernate.validator.cfg.defs all constraint definitions and in org.hibernate.validator.spi.cfg a callback for using the API for configuring the default validator factory. Refer to Section 11.4, “Programmatic constraint definition and declaration” for the details.

org.hibernate.validator.constraints, org.hibernate.validator.constraints.br

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 Section 2.3.2, “Additional constraints”.

org.hibernate.validator.constraintvalidation

Extended constraint validator context which allows to set custom attributes for message interpolation. Section 11.11.1, “HibernateConstraintValidatorContext describes how to make use of that feature.

org.hibernate.validator.group, org.hibernate.validator.spi.group

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 Section 5.3, “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 Section 4.2.1, “ResourceBundleLocator) and its default implementation.

org.hibernate.validator.parameternameprovider

A ParameterNameProvider based on the ParaNamer library, see Section 11.12, “ParaNamer based ParameterNameProvider.

org.hibernate.validator.propertypath

Extensions to the javax.validation.Path API, see Section 11.7, “Extensions of the Path API”.

org.hibernate.validator.spi.constraintdefinition

An SPI for registering additional constraint validators programmatically, see Section 11.14, “Providing constraint definitions”.

org.hibernate.validator.spi.time

An SPI for customizing the retrieval of the current time when validating @Future and @Past, see Section 11.16, “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 Section 11.13, “Unwrapping values”.


Note

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 API for programmatic constraint declaration or the custom constraints), the SPI (service provider interface) packages contain interfaces which are intended to be implemented by clients (e.g. ResourceBundleLocator).

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.

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.

Example 11.1, “Using the fail fast validation mode” shows how to bootstrap and use a fail fast enabled validator.

Example 11.1. Using the fail fast validation mode
package org.hibernate.validator.referenceguide.chapter11.failfast;

public class Car {

	@NotNull
	private String manufacturer;

	@AssertTrue
	private boolean isRegistered;

	public Car(String manufacturer, boolean isRegistered) {
		this.manufacturer = manufacturer;
		this.isRegistered = isRegistered;
	}

	//getters and setters...
}
Validator validator = Validation.byProvider( HibernateValidator.class )
		.configure()
		.failFast( true )
		.buildValidatorFactory()
		.getValidator();

Car car = new Car( null, false );

Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car );

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.

Note

There is no guarantee in which order the constraints are evaluated, i.e. it is not deterministic whether the returned violation originates from the @NotNull or the @AssertTrue constraint. If required, a deterministic evaluation order can be enforced using group sequences as described in Section 5.2, “Defining group sequences”.

Refer to Section 8.2.6, “Provider-specific settings” to learn about the different ways of enabling the fail fast mode when bootstrapping a validator.

11.3. Relaxation of requirements for method validation in class hierarchies

The Bean Validation specification defines a set of preconditions which apply when defining constraints on methods within class hierarchies. These preconditions are defined in section 4.5.5 of the Bean Validation 1.1 specification. See also Section 3.1.4, “Method constraints in inheritance hierarchies” in this guide.

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.

First you can use the configuration properties hibernate.validator.allow_parameter_constraint_override, hibernate.validator.allow_multiple_cascaded_validation_on_result and hibernate.validator.allow_parallel_method_parameter_constraint in validation.xml. See example Example 11.2, “Configuring method validation behaviour in class hierarchies via properties”.

Example 11.2. Configuring method validation behaviour in class hierarchies via properties
<?xml version="1.0" encoding="UTF-8"?>
<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 validation-configuration-1.0.xsd">
    <default-provider>org.hibernate.validator.HibernateValidator</default-provider>

    <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</property>
</validation-config>

Alternatively these settings can be applied during programmatic bootstrapping.

Example 11.3. Configuring method validation behaviour in class hierarchies
HibernateValidatorConfiguration configuration = Validation.byProvider(HibernateValidator.class).configure();

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.

Warning

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.

11.4. Programmatic constraint definition and declaration

As per the Bean Validation specification, you can define and declare constraints using Java annotations and XML based constraint mappings.

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 Example 11.4, “Programmatic constraint declaration”.

Example 11.4. Programmatic constraint declaration
HibernateValidatorConfiguration configuration = Validation
		.byProvider( HibernateValidator.class )
		.configure();

ConstraintMapping constraintMapping = configuration.createConstraintMapping();

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() );

Validator validator = configuration.addMapping( constraintMapping )
		.buildValidatorFactory()
		.getValidator();

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.

Note

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.

Note

It is not supported to add constraints to non-overridden supertype properties and methods by configuring a subtype. Instead you need to configure the supertype in this case.

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 Example 11.5, “Programmatic declaration of a custom constraint”.

Example 11.5. Programmatic declaration of a custom constraint
ConstraintMapping constraintMapping = configuration.createConstraintMapping();

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 Example 11.6, “Marking a property for cascaded validation”.

Example 11.6. Marking a property for cascaded validation
ConstraintMapping constraintMapping = configuration.createConstraintMapping();

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 constructor constraints. As shown in Example 11.7, “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.

Example 11.7. Programmatic declaration of method and constructor constraints
ConstraintMapping constraintMapping = configuration.createConstraintMapping();

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 11.8. Configuration of default group sequence and default group sequence provider
ConstraintMapping constraintMapping = configuration.createConstraintMapping();

constraintMapping
	.type( Car.class )
		.defaultGroupSequence( Car.class, CarChecks.class )
	.type( RentalCar.class )
		.defaultGroupSequenceProviderClass( RentalCarGroupSequenceProvider.class );

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 Chapter 7, 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:

Example 11.9. Custom ConstraintMappingContributor implementation
package org.hibernate.validator.referenceguide.chapter11.constraintapi;

public class MyConstraintMappingContributor implements ConstraintMappingContributor {

	@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.

11.6. Advanced constraint composition features

11.6.1. Validation target specification for purely composed constraints

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.

Hibernate Validator allows to resolve such ambiguities by specifying the @SupportedValidationTarget annotation on the declaration of the composed constraint type as shown in Example 11.10, “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.

Example 11.10. Specifying the validation target of a purely composed constraint
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 {

	String message() default "{org.hibernate.validator.referenceguide.chapter11.purelycomposed."
			+ "ValidInvoiceAmount.message}";

	Class<?>[] groups() default {};

	Class<? extends Payload>[] payload() default {};

	@OverridesAttribute(constraint = Min.class, name = "value")
	long value();
}

11.6.2. Boolean composition of constraints

Bean Validation specifies that the constraints of a composed constraint (see Section 6.4, “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.

Example 11.11, “OR composition of constraints” shows how to build a composed constraint @PatternOrSize where only one of the composing constraints needs to be valid in order to pass the validation. Either the validated string is all lower-cased or it is between two and three characters long.

Example 11.11. 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}";

	Class<?>[] groups() default { };

	Class<? extends Payload>[] payload() default { };
}

Tip

Using ALL_FALSE as composition type implicitly enforces that only a single violation will get reported in case validation of the constraint composition fails.

11.7. Extensions of the Path API

Hibernate Validator provides an extension to the javax.validation.Path API. For nodes of ElementKind.PROPERTY it allows to obtain the value of the represented property. To do so, narrow down a given node to the type org.hibernate.validator.path.PropertyNode using Node#as(), as shown in the following example:

Example 11.12. Getting the value from property nodes
Building building = new Building();

// Assume the name of the person violates a @Size constraint
Person bob = new Person( "Bob" );
Apartment bobsApartment = new Apartment( bob );
building.getApartments().add( bobsApartment );

Set<ConstraintViolation<Building>> constraintViolations = validator.validate( building );

Path path = constraintViolations.iterator().next().getPropertyPath();
Iterator<Path.Node> nodeIterator = path.iterator();

Path.Node node = nodeIterator.next();
assertEquals( node.getName(), "apartments" );
assertSame( node.as( PropertyNode.class ).getValue(), bobsApartment );

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).

11.8. Dynamic payload as part of ConstraintViolation

In some cases automatic processing of violations can be aided, if the constraint violation provides additional data - a so called dynamic payload. This dynamic payload could for example contain hints to the user on how to resolve the violation.

Dynamic payloads can be set in custom constraints using HibernateConstraintValidatorContext. This is shown in example Example 11.13, “ConstraintValidator implementation setting a dynamic payload” where the javax.validation.ConstraintValidatorContext is unwrapped to HibernateConstraintValidatorContext in order to call withDynamicPayload.

Example 11.13. ConstraintValidator implementation setting a dynamic payload
public class ValidPassengerCountValidator implements ConstraintValidator<ValidPassengerCount, Car> {

	private static final Map<Integer, String> suggestedCars = 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;
		}

		int passangerCount = car.getPassengers().size();
		if ( car.getSeatCount() >= passangerCount ) {
			return true;
		}
		else {

			if ( suggestedCars.containsKey( passangerCount ) ) {
				HibernateConstraintValidatorContext hibernateContext = context.unwrap(
						HibernateConstraintValidatorContext.class
				);
				hibernateContext.withDynamicPayload( suggestedCars.get( passangerCount ) );
			}
			return false;
		}
	}
}

On the constraint violation processing side, a javax.validation.ConstraintViolation can then in turn be unwrapped to HibernateConstraintViolation in order to retrieve the dynamic payload for further processing.

Example 11.14. Retrieval of a ConstraintViolations’s dynamic payload
@Test
public void testDynamicPayloadAddedToConstraintViolation() throws Exception {
	Car car = new Car( 2 );
	car.addPassenger( new Person() );
	car.addPassenger( new Person() );
	car.addPassenger( new Person() );
	Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car );

	assertEquals( 1, constraintViolations.size() );

	ConstraintViolation<Car> constraintViolation = constraintViolations.iterator().next();
	@SuppressWarnings("unchecked")
	HibernateConstraintViolation<Car> hibernateConstraintViolation = constraintViolation.unwrap(
			HibernateConstraintViolation.class
	);
	String suggestedCar = hibernateConstraintViolation.getDynamicPayload( String.class );
	assertEquals( "Toyota Volta", suggestedCar );
}

11.9. ParameterMessageInterpolator

Hibernate Validator requires per default an implementation of the Unified EL (see Section 1.1.1, “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 Section 4.2, “Custom message interpolation” to see how to plug in custom message interpolator implementations.

Warning

Constraint messages containing EL expressions will be returned un-interpolated by org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator. This also affects built-in default constraint messages which use EL expressions. At the moment DecimalMin and DecimalMax are affected.

11.10. ResourceBundleLocator

With ResourceBundleLocator, Hibernate Validator provides an additional SPI which allows to retrieve error messages from other resource bundles than ValidationMessages while still using the actual interpolation algorithm as defined by the specification. Refer to Section 4.2.1, “ResourceBundleLocator to learn how to make use of that SPI.

11.11. Custom contexts

The Bean Validation specification offers at several points in its API the possibility to unwrap a given interface to a implementor specific subtype. In the case of constraint violation creation in ConstraintValidator implementations as well as message interpolation in MessageInterpolator instances, there exist unwrap() methods for the provided context instances - ConstraintValidatorContext respectively MessageInterpolatorContext. Hibernate Validator provides custom extensions for both of these interfaces.

11.11.1. HibernateConstraintValidatorContext

HibernateConstraintValidatorContext is a subtype of ConstraintValidatorContext which allows you to:

  • set arbitrary parameters for interpolation via the Expression Language message interpolation facility using HibernateConstraintValidatorContext#addExpressionVariable(String, Object). For an example refer to Example 11.15, “Custom @Future validator with message parameters”.

    Note

    Note that the parameters specified via addExpressionVariable(String, Object) are global and apply for all constraint violations created by this isValid() invocation. This 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 Section 11.16, “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". Example 11.15, “Custom @Future validator with message parameters” shows how to include the current date in order to make the message more explicit.

    Example 11.15. Custom @Future validator with message parameters
    public class MyFutureValidator implements ConstraintValidator<Future, Date> {
    
    	@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
    		);
    
    		Date now = new Date( hibernateContext.getTimeProvider().getCurrentTime() );
    
    		if ( !value.after( now ) ) {
    			hibernateContext.disableDefaultConstraintViolation();
    			hibernateContext.addExpressionVariable( "now", now )
    					.buildConstraintViolationWithTemplate( "Must be after ${now}" )
    					.addConstraintViolation();
    
    			return false;
    		}
    
    		return true;
    	}
    }

    Warning

    This functionality is currently experimental and might change in future versions.

  • set an arbitrary dynamic payload - see Section 11.8, “Dynamic payload as part of ConstraintViolation”

11.11.2. HibernateMessageInterpolatorContext

Hibernate Validator also offers a custom extension of MessageInterpolatorContext, namely HibernateMessageInterpolatorContext (see Example 11.16, “HibernateMessageInterpolatorContext). This subtype was introduced to allow a better integration of Hibernate Validator into the Glassfish. The root bean type was in this case needed to determine the right classloader for the message resource bundle. If you have any other usecases, let us know.

Example 11.16. HibernateMessageInterpolatorContext
public interface HibernateMessageInterpolatorContext extends MessageInterpolator.Context {

	/**
	 * Returns the currently validated root bean type.
	 *
	 * @return The currently validated root bean type.
	 */
	Class<?> getRootBeanType();
}

11.12. ParaNamer based ParameterNameProvider

Hibernate Validator comes with a ParameterNameProvider implementation which leverages the ParaNamer library.

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.

In order to use ParanamerParameterNameProvider, either pass an instance when bootstrapping a validator as shown in Example 8.8, “Using a custom ParameterNameProvider or specify org.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider as value for the <parameter-name-provider> element in the META-INF/validation.xml file.

Tip

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.

By default ParanamerParameterNameProvider retrieves parameter names from constants added to the byte code at build time (via DefaultParanamer) and debug symbols (via BytecodeReadingParanamer). Alternatively you can specify a Paranamer implementation of your choice when creating a ParanamerParameterNameProvider instance.

11.13. Unwrapping values

Sometimes it is required to unwrap values prior to validating them. For example, in Example 11.17, “Applying a 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.

Example 11.17. Applying a constraint to wrapped value of a JavaFX property
@Size(min = 3)
private Property<String> name = new SimpleStringProperty( "Bob" );

Note

The concept of value unwrapping is considered experimental at this time 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.

For unwrapping to occur a ValidatedValueUnwrapper needs to be registered for the type requiring unwrapping. Example Example 11.18, “Implementing the ValidatedValueUnwrapper interface” shows how this schematically looks for a JavaFX PropertyValueUnwrapper. You just need to extend the SPI class ValidatedValueUnwrapper and implement its abstract methods.

Example 11.18. Implementing the ValidatedValueUnwrapper interface
public class PropertyValueUnwrapper extends ValidatedValueUnwrapper<Property<?>> {

	@Override
	public Object handleValidatedValue(Property<?> value) {
		//...
	}

	@Override
	public Type getValidatedValueType(Type valueType) {
		//...
	}
}

The ValidatedValueUnwrapper needs also to be registered with the ValidatorFactory:

Example 11.19. Registering a ValidatedValueUnwrapper
Validator validator = Validation.byProvider( HibernateValidator.class )
		.configure()
		.addValidatedValueHandler( new PropertyValueUnwrapper() )
		.buildValidatorFactory()
		.getValidator();

Several unwrapper implementations can be registered. During constraint validator resolution Hibernate Validator automatically checks whether a ValidatedValueUnwrapper exists for the validated value. If so, unwrapping occurs automatically. In some cases, however, constraint validator instances for a given constraint might exist for the wrapper as well as the wrapped value (@NotNull for example applies to all objects). In this case Hibernate Validator needs to be explicitly told which value to validate. This can be done via @UnwrapValidatedValue(true) respectively @UnwrapValidatedValue(false).

Note

Note that it is not specified which of the unwrapper implementations is chosen when more than one implementation is suitable to unwrap a given element.

Instead of programmatically registering ValidatedValueUnwrapper types, the fully-qualified names of one ore more unwrapper implementations can be specified via the configuration property hibernate.validator.validated_value_handlers which can be useful when configuring the default validator factory using the descriptor META-INF/validation.xml (see Chapter 7, Configuring via XML).

11.13.1. Optional unwrapper

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 Example 11.20, “Unwrapping Optional instances”.

Example 11.20. 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" );

Note

Optional.empty() is treated as null during validation. This means that for constraints where null is considered valid, Optional.empty() is similarly valid.

11.13.2. JavaFX unwrapper

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 Example 11.21, “Unwrapping JavaFX properties”.

Example 11.21. 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 );

11.13.3. Unwrapping object graphs

Unwrapping can also be used with object graphs (cascaded validation) as shown in Example 11.22, “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.

Example 11.22. Unwrapping Optional prior to cascaded validation via @Valid
@Valid
private Optional<Person> person = Optional.of( new Person() );
public class Person {
	@Size(min =3)
	private String name = "Bob";
}

11.14. Providing constraint definitions

Bean Validation allows to (re-)define constraint definitions via XML in its constraint mapping files. See Section 7.2, “Mapping constraints via constraint-mappings for more information and Example 7.2, “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.

Note

The following concepts are considered experimental at this time. Let us know whether you find them useful and whether they meet your needs.

11.14.1. Constraint definitions via ServiceLoader

Hibernate Validator allows to utilize Java’s ServiceLoader mechanism to register additional constraint definitions. All you have to do is to add the file javax.validation.ConstraintValidator to META-INF/services. In this service file you list the fully qualified classnames of your constraint validator classes (one per line). Hibernate Validator will automatically infer the constraint types they apply to. See Constraint definition via service file for an example.

Example 11.23. META-INF/services/javax.validation.ConstraintValidator
# Assuming a custom constraint annotation @org.mycompany.CheckCase
org.mycompany.CheckCaseValidator

To contribute default messages for your custom constraints, place a file ContributorValidationMessages.properties and/or its locale-specific specializations at the root your JAR. Hibernate Validator will consider the entries from all the bundles with this name found on the classpath in addition to those given in ValidationMessages.properties.

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.

11.14.2. Adding constraint definitions programmatically

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 Example 11.24, “Adding constraint definitions through the programmatic API”.

Example 11.24. Adding constraint definitions through the programmatic API
ConstraintMapping constraintMapping = configuration.createConstraintMapping();

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 Section 11.5, “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 Chapter 7, Configuring via XML).

Tip

One use case for registering constraint definitions through the programmatic API is the ability to specify an alternative constraint validator for the @URL constraint. Historically, Hibernate Validator’s default constraint validator for this constraint uses the java.net.URL constructor to validate an URL. However, there is also a purely regular expression based version available which can be configured using a ConstraintDefinitionContributor:

Using the programmatic constraint declaration API to register a regular expression based constraint definition for @URL

ConstraintMapping constraintMapping = configuration.createConstraintMapping();

constraintMapping
		.constraintDefinition( URL.class )
		.includeExistingValidators( false )
		.validatedBy( RegexpURLValidator.class );

11.15. Customizing class-loading

There are several cases in which Hibernate Validator needs to load resources or classes given by name:

  • XML descriptors (META-INF/validation.xml as well as XML constraint mappings)
  • classes specified by name in XML descriptors (e.g. custom message interpolators etc.)
  • the ValidationMessages resource bundle
  • the ExpressionFactory implementation used for expression based message interpolation

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 11.25. Providing a classloader for loading external resources and classes
ClassLoader classLoader = ...;

Validator validator = Validation.byProvider( HibernateValidator.class )
		.configure()
		.externalClassLoader( classLoader )
		.buildValidatorFactory()
		.getValidator();

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.

Note

Call ValidatorFactory#close() if a given validator factory instance is not needed any longer. Failure to do so may result in a classloader leak in cases where applications/bundles are re-deployed and a non-closed validator factory still is referenced by application code.

11.16. Time providers for @Future and @Past

By default the current system time is used when validating the @Future and @Past constraints. In some cases it can be necessary though to work with another "logical" date rather than the system time, e.g. for testing purposes or in the context of batch applications which may require to run with yesterday’s date when re-running a failed job execution.

To address such scenarios, Hibernate Validator provides a custom contract for obtaining the current time, TimeProvider. Example 11.26, “Using a custom TimeProvider shows an implementation of this contract and its registration when bootstrapping a validator factory.

Example 11.26. Using a custom TimeProvider
public class CustomTimeProvider implements TimeProvider {

	@Override
	public long getCurrentTime() {
		Calendar now = ...;
		return now.getTimeInMillis();
	}
}
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
		.configure()
		.timeProvider( timeProvider )
		.buildValidatorFactory();

Alternatively, you can specify the fully-qualified classname of a TimeProvider implementation using the property hibernate.validator.time_provider when configuring the default validator factory via META-INF/validation.xml (see Chapter 7, Configuring via XML).