Hibernate.orgCommunity Documentation
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 9.1, “Example classes”.
Example 9.1. Example classes
package org.hibernate.validator.referenceguide.chapter07;
public class Person {
public interface Basic {
}
@NotNull
private String name;
//getters and setters ...
}
public interface Vehicle {
public interface Basic {
}
@NotNull(groups = Vehicle.Basic.class)
String getManufacturer();
}
@ValidCar
public class Car implements Vehicle {
public interface SeverityInfo extends Payload {
}
private String manufacturer;
@NotNull
@Size(min = 2, max = 14)
private String licensePlate;
private Person driver;
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;
}
public void driveAway(@Max(75) int speed) {
//...
}
@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;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
@Valid
@ConvertGroup(from = Default.class, to = Person.Basic.class)
public Person getDriver() {
return driver;
}
//further getters and setters...
}
The entry point into the metadata API is the method
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.
Example 9.2, “Using BeanDescriptor” demonstrates
how to retrieve a BeanDescriptor
for the
Car
class and how to use this descriptor in form of
assertions.
If a constraint declaration hosted by the requested class is
invalid, a ValidationException
is
thrown.
Example 9.2. Using BeanDescriptor
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
BeanDescriptor carDescriptor = validator.getConstraintsForClass( Car.class );
assertTrue( carDescriptor.isBeanConstrained() );
//one class-level constraint
assertEquals( 1, carDescriptor.getConstraintDescriptors().size() );
//manufacturer, licensePlate, driver
assertEquals( 3, carDescriptor.getConstrainedProperties().size() );
//property has constraint
assertNotNull( carDescriptor.getConstraintsForProperty( "licensePlate" ) );
//property is marked with @Valid
assertNotNull( carDescriptor.getConstraintsForProperty( "driver" ) );
//constraints from getter method in interface and implementation class are returned
assertEquals(
2,
carDescriptor.getConstraintsForProperty( "manufacturer" )
.getConstraintDescriptors()
.size()
);
//property is not constrained
assertNull( carDescriptor.getConstraintsForProperty( "modelName" ) );
//driveAway(int), load(List<Person>, List<PieceOfLuggage>)
assertEquals( 2, carDescriptor.getConstrainedMethods( MethodType.NON_GETTER ).size() );
//driveAway(int), getManufacturer(), getDriver(), load(List<Person>, List<PieceOfLuggage>)
assertEquals(
4,
carDescriptor.getConstrainedMethods( MethodType.NON_GETTER, MethodType.GETTER )
.size()
);
//driveAway(int)
assertNotNull( carDescriptor.getConstraintsForMethod( "driveAway", int.class ) );
//getManufacturer()
assertNotNull( carDescriptor.getConstraintsForMethod( "getManufacturer" ) );
//setManufacturer() is not constrained
assertNull( carDescriptor.getConstraintsForMethod( "setManufacturer", String.class ) );
//Car(String, String, Person, String)
assertEquals( 1, carDescriptor.getConstrainedConstructors().size() );
//Car(String, String, Person, String)
assertNotNull(
carDescriptor.getConstraintsForConstructor(
String.class,
String.class,
Person.class,
String.class
)
);
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()
.
The method getConstraintDescriptors()
is common to all
descriptors derived from ElementDescriptor
(see
Section 9.4, “ElementDescriptor”) and returns a
set of descriptors representing the constraints directly declared on the
given element. In case of BeanDescriptor
, the
bean's class-level constraints are returned. More details on
ConstraintDescriptor
can be found in Section 9.6, “ConstraintDescriptor”.
Via getConstraintsForProperty()
,
getConstraintsForMethod()
and
getConstraintsForConstructor()
you can obtain a
descriptor representing one given property or executable element,
identified by its name and, in case of methods and constructors, parameter
types. The different descriptor types returned by these methods are
described in the following sections.
Note that these methods consider constraints declared at super-types
according to the rules for constraint inheritance as described in Section 2.1.4, “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.
The methods getConstrainedProperties()
,
getConstrainedMethods()
and
getConstrainedConstructors()
return (potentially
empty) sets with all constrained properties, methods and constructors,
respectively. An element is considered constrained, if it 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).
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. Example 9.3, “Using PropertyDescriptor” shows how to use the
PropertyDescriptor
interface.
Example 9.3. Using PropertyDescriptor
PropertyDescriptor licensePlateDescriptor = carDescriptor.getConstraintsForProperty(
"licensePlate"
);
//"licensePlate" has two constraints, is not marked with @Valid and defines no group conversions
assertEquals( "licensePlate", licensePlateDescriptor.getPropertyName() );
assertEquals( 2, licensePlateDescriptor.getConstraintDescriptors().size() );
assertTrue( licensePlateDescriptor.hasConstraints() );
assertFalse( licensePlateDescriptor.isCascaded() );
assertTrue( licensePlateDescriptor.getGroupConversions().isEmpty() );
PropertyDescriptor driverDescriptor = carDescriptor.getConstraintsForProperty( "driver" );
//"driver" has no constraints, is marked with @Valid and defines one group conversion
assertEquals( "driver", driverDescriptor.getPropertyName() );
assertTrue( driverDescriptor.getConstraintDescriptors().isEmpty() );
assertFalse( driverDescriptor.hasConstraints() );
assertTrue( driverDescriptor.isCascaded() );
assertEquals( 1, driverDescriptor.getGroupConversions().size() );
Using getConstrainedDescriptors()
, you can
retrieve a set of ConstraintDescriptor
s providing
more information on the individual constraints of a given property. The
method isCascaded()
returns
true
, if the property is marked for cascaded validation
(either using the @Valid
annotation or via XML),
false
otherwise. Any configured group conversions are
returned by getGroupConversions()
. See Section 9.5, “GroupConversionDescriptor” for more details on
GroupConversionDescriptor
.
Constrained methods and constructors are represented by the
interfaces MethodDescriptor
and ConstructorDescriptor
,
respectively. Example 9.4, “Using MethodDescriptor and
ConstructorDescriptor”
demonstrates how to work with these descriptors.
Example 9.4. Using MethodDescriptor
and
ConstructorDescriptor
//driveAway(int) has a constrained parameter and an unconstrained return value
MethodDescriptor driveAwayDescriptor = carDescriptor.getConstraintsForMethod(
"driveAway",
int.class
);
assertEquals( "driveAway", driveAwayDescriptor.getName() );
assertTrue( driveAwayDescriptor.hasConstrainedParameters() );
assertFalse( driveAwayDescriptor.hasConstrainedReturnValue() );
//always returns an empty set; constraints are retrievable by navigating to
//one of the sub-descriptors, e.g. for the return value
assertTrue( driveAwayDescriptor.getConstraintDescriptors().isEmpty() );
ParameterDescriptor speedDescriptor = driveAwayDescriptor.getParameterDescriptors()
.get( 0 );
//The "speed" parameter is located at index 0, has one constraint and is not cascaded
//nor does it define group conversions
assertEquals( "arg0", speedDescriptor.getName() );
assertEquals( 0, speedDescriptor.getIndex() );
assertEquals( 1, speedDescriptor.getConstraintDescriptors().size() );
assertFalse( speedDescriptor.isCascaded() );
assert speedDescriptor.getGroupConversions().isEmpty();
//getDriver() has no constrained parameters but its return value is marked for cascaded
//validation and declares one group conversion
MethodDescriptor getDriverDescriptor = carDescriptor.getConstraintsForMethod(
"getDriver"
);
assertFalse( getDriverDescriptor.hasConstrainedParameters() );
assertTrue( getDriverDescriptor.hasConstrainedReturnValue() );
ReturnValueDescriptor returnValueDescriptor = getDriverDescriptor.getReturnValueDescriptor();
assertTrue( returnValueDescriptor.getConstraintDescriptors().isEmpty() );
assertTrue( returnValueDescriptor.isCascaded() );
assertEquals( 1, returnValueDescriptor.getGroupConversions().size() );
//load(List<Person>, List<PieceOfLuggage>) has one cross-parameter constraint
MethodDescriptor loadDescriptor = carDescriptor.getConstraintsForMethod(
"load",
List.class,
List.class
);
assertTrue( loadDescriptor.hasConstrainedParameters() );
assertFalse( loadDescriptor.hasConstrainedReturnValue() );
assertEquals(
1,
loadDescriptor.getCrossParameterDescriptor().getConstraintDescriptors().size()
);
//Car(String, String, Person, String) has one constrained parameter
ConstructorDescriptor constructorDescriptor = carDescriptor.getConstraintsForConstructor(
String.class,
String.class,
Person.class,
String.class
);
assertEquals( "Car", constructorDescriptor.getName() );
assertFalse( constructorDescriptor.hasConstrainedReturnValue() );
assertTrue( constructorDescriptor.hasConstrainedParameters() );
assertEquals(
1,
constructorDescriptor.getParameterDescriptors()
.get( 0 )
.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.
These descriptors provide access to the element's constraints
(getConstraintDescriptors()
) and, in case of
parameters and return value, to its configuration for cascaded validation
(isValid()
and
getGroupConversions()
). For parameters, you also
can retrieve the index and the name, as returned by the currently used
parameter name provider (see Section 8.2.4, “ParameterNameProvider”) via
getName()
and
getIndex()
.
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").getReturnValueDescriptor()
).
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
the object type when invoked on
BeanDescriptor
,
the type of a property or parameter when invoked on
PropertyDescriptor
or
ParameterDescriptor
respectively,
Object[].class
when invoked on
CrossParameterDescriptor
,
the return type when invoked on
ConstructorDescriptor
,
MethodDescriptor
or
ReturnValueDescriptor
.
void.class
will be returned for methods which
don't have a return value.
Example 9.5, “Using ElementDescriptor methods” shows how these methods are used.
Example 9.5. Using ElementDescriptor
methods
PropertyDescriptor manufacturerDescriptor = carDescriptor.getConstraintsForProperty(
"manufacturer"
);
assertTrue( manufacturerDescriptor.hasConstraints() );
assertEquals( String.class, manufacturerDescriptor.getElementClass() );
CrossParameterDescriptor loadCrossParameterDescriptor = carDescriptor.getConstraintsForMethod(
"load",
List.class,
List.class
).getCrossParameterDescriptor();
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. Example 9.6, “Usage of ConstraintFinder” shows how to retrieve a
ConstraintFinder
instance via
findConstraints()
and use the API to query for
constraint metadata.
Example 9.6. Usage of ConstraintFinder
PropertyDescriptor manufacturerDescriptor = carDescriptor.getConstraintsForProperty(
"manufacturer"
);
//"manufacturer" constraints are declared on the getter, not the field
assertTrue(
manufacturerDescriptor.findConstraints()
.declaredOn( ElementType.FIELD )
.getConstraintDescriptors()
.isEmpty()
);
//@NotNull on Vehicle#getManufacturer() is part of another group
assertEquals(
1,
manufacturerDescriptor.findConstraints()
.unorderedAndMatchingGroups( Default.class )
.getConstraintDescriptors()
.size()
);
//@Size on Car#getManufacturer()
assertEquals(
1,
manufacturerDescriptor.findConstraints()
.lookingAt( Scope.LOCAL_ELEMENT )
.getConstraintDescriptors()
.size()
);
//@Size on Car#getManufacturer() and @NotNull on Vehicle#getManufacturer()
assertEquals(
2,
manufacturerDescriptor.findConstraints()
.lookingAt( Scope.HIERARCHY )
.getConstraintDescriptors()
.size()
);
//Combining several filter options
assertEquals(
1,
manufacturerDescriptor.findConstraints()
.declaredOn( ElementType.METHOD )
.lookingAt( Scope.HIERARCHY )
.unorderedAndMatchingGroups( Vehicle.Basic.class )
.getConstraintDescriptors()
.size()
);
Via declaredOn()
you can search for
ConstraintDescriptor
s declared on certain element
types. This is useful to find property constraints declared on either
fields or getter methods.
unorderedAndMatchingGroups()
restricts the
resulting constraints to those matching the given validation
group(s).
lookingAt()
allows to distinguish between
constraints directly specified on the element
(Scope.LOCAL_ELEMENT
) or constraints belonging to the
element but hosted anywhere in the class hierarchy
(Scope.HIERARCHY
).
You can also combine the different options as shown in the last example.
Order is not respected by
unorderedAndMatchingGroups()
, but group
inheritance and inheritance via sequence are.
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. Example 9.7, “Using GroupConversionDescriptor” shows an
example.
Example 9.7. Using GroupConversionDescriptor
PropertyDescriptor driverDescriptor = carDescriptor.getConstraintsForProperty( "driver" );
Set<GroupConversionDescriptor> groupConversions = driverDescriptor.getGroupConversions();
assertEquals( 1, groupConversions.size() );
GroupConversionDescriptor groupConversionDescriptor = groupConversions.iterator()
.next();
assertEquals( Default.class, groupConversionDescriptor.getFrom() );
assertEquals( Person.Basic.class, groupConversionDescriptor.getTo() );
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.
Example 9.8, “Using ConstraintDescriptor” shows
how to retrieve default constraint attributes (such as message template,
groups etc.) as well as custom constraint attributes
(piecesOfLuggagePerPassenger
) and other metadata such
as the constraint's annotation type and its validators from a
ConstraintDescriptor
.
Example 9.8. Using ConstraintDescriptor
//descriptor for the @LuggageCountMatchesPassengerCount constraint on the
//load(List<Person>, List<PieceOfLuggage>) method
ConstraintDescriptor<?> constraintDescriptor = carDescriptor.getConstraintsForMethod(
"load",
List.class,
List.class
).getCrossParameterDescriptor().getConstraintDescriptors().iterator().next();
//constraint type
assertEquals(
LuggageCountMatchesPassengerCount.class,
constraintDescriptor.getAnnotation().annotationType()
);
//standard constraint attributes
assertEquals( SeverityInfo.class, constraintDescriptor.getPayload().iterator().next() );
assertEquals(
ConstraintTarget.PARAMETERS,
constraintDescriptor.getValidationAppliesTo()
);
assertEquals( Default.class, constraintDescriptor.getGroups().iterator().next() );
assertEquals(
"There must not be more than {piecesOfLuggagePerPassenger} pieces of luggage per " +
"passenger.",
constraintDescriptor.getMessageTemplate()
);
//custom constraint attribute
assertEquals(
2,
constraintDescriptor.getAttributes().get( "piecesOfLuggagePerPassenger" )
);
//no composing constraints
assertTrue( constraintDescriptor.getComposingConstraints().isEmpty() );
//validator class
assertEquals(
Arrays.<Class<?>>asList( LuggageCountMatchesPassengerCount.Validator.class ),
constraintDescriptor.getConstraintValidatorClasses()
);
Copyright © 2009 - 2013 Red Hat, Inc. & Gunnar Morling