Hibernate.org Community Documentation

- 上一页
- 下一页

## 第 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.

## 注意

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

表 11.1. Hibernate Validator public API

Packages | Description
--- | ---
org.hibernate.validator | Classes used by the Bean Validation bootstrap mechanism (eg. validation provider, configuration class); For more details see 第 8 章 Bootstrapping.
org.hibernate.validator.cfg, org.hibernate.validator.cfg.context, org.hibernate.validator.cfg.defs | Hibernate Validator's fluent API for constraint declaration; In org.hibernate.validator.cfg you will find the `ConstraintMapping` interface and in org.hibernate.validator.cfg.defs all constraint definitions. Refer to 第 11.3 节 “Programmatic constraint 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 第 2.3.2 节 “Additional constraints”.
org.hibernate.validator.constraintvalidation | Extended constraint validator context which allows to set custom attributes for message interpolation. 第 11.6.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 第 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 第 4.2.1 节 “ResourceBundleLocator”) and its default implementation.
org.hibernate.validator.parameternameprovider | A `ParameterNameProvider` based on the ParaNamer library, see 第 11.7 节 “ParaNamer based ParameterNameProvider”.
org.hibernate.validator.valuehandling, org.hibernate.validator.spi.valuehandling | Classes related to the processing of values prior to their validation, see 第 11.8 节 “Unwrapping values prior to validation”.

## 注意

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.

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

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

## 注意

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 第 5.2 节 “Defining group sequences”.

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

## 11.3. Programmatic constraint declaration

As per the Bean Validation specification, you can 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 例 11.2 “Programmatic constraint declaration”.

例 11.2. 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.

## 注意

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.

## 注意

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 例 11.3 “Programmatic declaration of a custom constraint”.

例 11.3. 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 例 11.4 “Marking a property for cascaded validation”.

例 11.4. 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 例 11.5 “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.

例 11.5. 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.

例 11.6. 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.4. Boolean composition of constraints

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

例 11.7 “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.

例 11.7. 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 { };

    }

## 提示

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.5. `ResourceBundleLocator`

As described in 第 4.2 节 “Custom message interpolation”, Bean Validation allows to plug in custom message interpolator implementations.

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 第 4.2.1 节 “ResourceBundleLocator” to learn how to make use of that SPI.

## 11.6. 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.6.1. `HibernateConstraintValidatorContext`

`HibernateConstraintValidatorContext` is a subtype of `ConstraintValidatorContext` which allows you to set arbitrary parameters for interpolation via the Expression Language message interpolation facility (see 第 4.1.2 节 “Interpolation with message expressions”). For example the default error message for the `@Future` constraint is "must be in the future". What if you would like to include the current date to make the message more explicit? 例 11.8 “Custom @Future validator with message parameters” shows how this could be achieved.

例 11.8. Custom `@Future` validator with message parameters

    public class MyFutureValidator implements ConstraintValidator<Future, Date> {

            public void initialize(Future constraintAnnotation) {

            }

            public boolean isValid(Date value, ConstraintValidatorContext context) {

                    Date now = GregorianCalendar.getInstance().getTime();

                    if ( value.before( now ) ) {

                            HibernateConstraintValidatorContext hibernateContext =

                                            context.unwrap( HibernateConstraintValidatorContext.class );

                            hibernateContext.disableDefaultConstraintViolation();

                            hibernateContext.addExpressionVariable( "now", now )

                                            .buildConstraintViolationWithTemplate( "Must be after ${now}" )

                                            .addConstraintViolation();

                            return false;

                    }

                    return true;

            }

    }

## 注意

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

## 警告

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

### 11.6.2. `HibernateMessageInterpolatorContext`

Hibernate Validator also offers a custom extension of `MessageInterpolatorContext`, namely `HibernateMessageInterpolatorContext` (see 例 11.9 “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.

例 11.9. `HibernateMessageInterpolatorContext`

    public interface HibernateMessageInterpolatorContext extends MessageInterpolator.Context {

            /**

             * Returns the currently validated root bean type.

             *

             * @return The currently validated root bean type.

             */

            Class<?> getRootBeanType();

    }

## 11.7. 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 例 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.

## 提示

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.8. Unwrapping values prior to validation

Sometimes it is required to unwrap values prior to the validation. E.g. in 例 11.10 “Using@UnwrapValidatedValue” property types as specified by JavaFX are used to define an element of some domain model.

例 11.10. Using`@UnwrapValidatedValue`

    @Size(min = 3)

    @UnwrapValidatedValue

    private Property<String> name = new SimpleStringProperty( "Bob" );

## 注意

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.

In JavaFX, bean properties 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.

To do so, put the `@UnwrapValidatedValue` annotation to the element in question. This will advice the validation engine to look for an unwrapper implementation which returns the data type to be used for constraint validator resolution and unwraps the validated value. Unwrapper types must extend the SPI class `ValidatedValueUnwrapper` as shown in 例 11.11 “Implementing the ValidatedValueUnwrapper interface”.

例 11.11. Implementing the `ValidatedValueUnwrapper` interface

    public class PropertyValueUnwrapper extends ValidatedValueUnwrapper<Property<?>> {

            @Override

            public Object handleValidatedValue(Property<?> value) {

                    //...

            }

            @Override

            public Type getValidatedValueType(Type valueType) {

                    //...

            }

    }

Value unwrappers must be registered when obtaining a `Validator` instance as follows:

例 11.12. Registering a `ValidatedValueUnwrapper`

    Validator validator = Validation.byProvider( HibernateValidator.class )

                    .configure()

                    .addValidatedValueHandler( new PropertyValueUnwrapper() )

                    .buildValidatorFactory()

                    .getValidator();

Several unwrapper implementations can be registered when working with different kinds of wrapper types in one application. 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.

Alternatively, 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 第 7 章 Configuring via XML).

版权 © 2009 - 2013 Red Hat, Inc. & Gunnar Morling

- 上一页第 10 章 Integrating with other frameworks
- 上一级
- 起始页
- 下一页第 12 章 Annotation Processor