Hibernate.orgCommunity Documentation
在本章中,我们会详细的介绍如何使用Hibernate Validator 来对一个给定的实体模型进行验证. 还会介绍Bean Validation规范提供了哪些默认的约束条件和Hibernate Validator提供了哪些额外的. 让我们先从如何给一个实体添加约束开始.
Bean Validation 的约束是通过Java 注解(annotations)来标注的. 在本节中,我们会介绍如何使用这些注解(annotations)来标注一个实体模型. 并且,我们会区分三种不通的注解(annotations) 类型.
不是所有的约束都能够被用在所有的类结构上. 事实上, 没有任何定义在Bean Validation规范中的约束可以被用在class上. 约束定义中的java.lang.annotation.Target
属性定义了这个约束能够被使用在哪个层次结构上. 详细信息请参考第 3 章 创建自己的约束规则.
约束条件能够被标注在类的字段上面, 请参考示例例 2.1 “字段级(field level) 约束”
例 2.1. 字段级(field level) 约束
package com.mycompany;
import javax.validation.constraints.NotNull;
public class Car {
@NotNull
private String manufacturer;
@AssertTrue
private boolean isRegistered;
public Car(String manufacturer, boolean isRegistered) {
super();
this.manufacturer = manufacturer;
this.isRegistered = isRegistered;
}
}
当约束被定义在字段上的时候, 这个字段的值是通过字段访问策略来获取并验证的. 也就是说Bean Validation的实现者会直接访问这个实例变量而不会调用属性的访问器(getter) 即使这个方法存在.
这个字段的访问级别( private, protected 或者 public) 对此没有影响.
静态字段或者属性是不会被校验的.
When validating byte code enhanced objects property level constraints should be used, because the byte code enhancing library won't be able to determine a field access via reflection.
如果你的模型遵循JavaBeans规范的话, 你还可以把约束标注在属性上. 例 2.2 “属性级约束”和例 2.1 “字段级(field level) 约束”的唯一不同就是它的约束是定义在属性级别上的.
如果要定义约束在属性级别上的话,那么只能定义在访问器(getter)上面,不能定义在修改器(setter)上.
例 2.2. 属性级约束
package com.mycompany;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotNull;
public class Car {
private String manufacturer;
private boolean isRegistered;
public Car(String manufacturer, boolean isRegistered) {
super();
this.manufacturer = manufacturer;
this.isRegistered = isRegistered;
}
@NotNull
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
@AssertTrue
public boolean isRegistered() {
return isRegistered;
}
public void setRegistered(boolean isRegistered) {
this.isRegistered = isRegistered;
}
}
When using property level constraints property access strategy is used to access the value to be validated. This means the bean validation provider accesses the state via the property accessor method. One advantage of annotating properties instead of fields is that the constraints become part of the constrained type's API that way and users are aware of the existing constraints without having to examine the type's implementation.
It is recommended to stick either to field or property annotations within one class. It is not recommended to annotate a field and the accompanying getter method as this would cause the field to be validated twice.
Last but not least, a constraint can also be placed on class level. When a constraint annotation is placed on this level the class instance itself passed to the ConstraintValidator
. Class level constraints are useful if it is necessary to inspect more than a single property of the class to validate it or if a correlation between different state variables has to be evaluated. In 例 2.3 “类级别约束” we add the property passengers to the class Car
. We also add the constraint PassengerCount
on the class level. We will later see how we can actually create this custom constraint (see 第 3 章 创建自己的约束规则). For now it is enough to know that PassengerCount
will ensure that there cannot be more passengers in a car than there are seats.
例 2.3. 类级别约束
package com.mycompany;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@PassengerCount
public class Car {
@NotNull
private String manufacturer;
@NotNull
@Size(min = 2, max = 14)
private String licensePlate;
@Min(2)
private int seatCount;
private List<Person> passengers;
public Car(String manufacturer, String licencePlate, int seatCount) {
this.manufacturer = manufacturer;
this.licensePlate = licencePlate;
this.seatCount = seatCount;
}
//getters and setters ...
}
如果要验证的对象继承于某个父类或者实现了某个接口,那么定义在父类或者接口中的约束会在验证这个对象的时候被自动加载,如同这些约束定义在这个对象所在的类中一样. 让我们来看看下面的示例:
例 2.4. 约束继承
package com.mycompany;
import javax.validation.constraints.NotNull;
public class RentalCar extends Car {
private String rentalStation;
public RentalCar(String manufacturer, String rentalStation) {
super(manufacturer);
this.rentalStation = rentalStation;
}
@NotNull
public String getRentalStation() {
return rentalStation;
}
public void setRentalStation(String rentalStation) {
this.rentalStation = rentalStation;
}
}
我们有了一个新的RentalCar
类继承自前面我们已经见到的Car
, 这个子类中增加了一个rentalStation属性. 如果校验一个RentalCar
的实例对象, 那么不仅会验证属性rentalStation上的 @NotNull
约束是否合法,还会校验父类中的manufacturer属性.
如果类Car
是一个接口类型的话也是一样的效果.
如果类RentalCar
重写了父类Car
的getManufacturer()
方法, 那么定义在父类的这个方法上的约束和子类这个方法上定义的约束都会被校验.
Bean Validation API不仅能够用来校验单个的实例对象,还能够用来校验完整的对象图.要使用这个功能,只需要在一个有关联关系的字段或者属性上标注@Valid
. 这样,如果一个对象被校验,那么它的所有的标注了@Valid
的关联对象都会被校验. 请看例 2.6 “Adding a driver to the car”.
例 2.5. Class Person
package com.mycompany;
import javax.validation.constraints.NotNull;
public class Person {
@NotNull
private String name;
public Person(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
例 2.6. Adding a driver to the car
package com.mycompany;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
public class Car {
@NotNull
@Valid
private Person driver;
public Car(Person driver) {
this.driver = driver;
}
//getters and setters ...
}
如果校验Car
的实例对象的话,因为它的driver属性标注了@Valid
, 那么关联的Person
也会被校验. 所以,如果对象Person
的name属性如果是null
的话,那么校验会失败.
关联校验也适用于集合类型的字段, 也就是说,任何下列的类型:
数组
实现了java.lang.Iterable
接口( 例如Collection
, List
和 Set
)
实现了java.util.Map
接口
如果标注了@Valid
, 那么当主对象被校验的时候,这些集合对象中的元素都会被校验.
例 2.7. Car with a list of passengers
package com.mycompany;
import java.util.ArrayList;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
public class Car {
@NotNull
@Valid
private List<Person> passengers = new ArrayList<Person>();
public Car(List<Person> passengers) {
this.passengers = passengers;
}
//getters and setters ...
}
当校验一个Car
的实例的时候,如果passengers list中包含的任何一个Person
对象没有名字的话,都会导致校验失败(a ConstraintValidation
will be created).
对象图校验的时候是会被忽略null
值的.
Validator
是Bean Validation中最主要的接口, 我们会在第 5.1 节 “Configuration 和 ValidatorFactory”中详细介绍如何获取一个Validator
的实例, 现在先让我们来看看如何使用Validator
接口中的各个方法.
对一个实体对象验证之前首先需要有个Validator
对象, 而这个对象是需要通过Validation
类和 ValidatorFactory
来创建的. 最简单的方法是调用Validation.buildDefaultValidatorFactory()
这个静态方法.
例 2.8. Validation.buildDefaultValidatorFactory()
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
第 5 章 Bootstrapping介绍了其他的获取Validator实例的方法. 现在我们的目标是学习如何使用Validator
来校验实体对象.
Validator
中有三个方法能够被用来校验整个实体对象或者实体对象中的属性.
这三个方法都会返回一个Set<ConstraintViolation>
对象, 如果整个验证过程没有发现问题的话,那么这个set是空的, 否则, 每个违反约束的地方都会被包装成一个ConstraintViolation
的实例然后添加到set当中.
All the validation methods have a var-args parameter which can be used to specify, which validation groups shall be considered when performing the validation. If the parameter is not specified the default validation group (javax.validation.groups.Default
) will be used. We will go into more detail on the topic of validation groups in 第 2.3 节 “校验组”
使用validate()
方法对一个给定的实体对象中定义的所有约束条件进行校验 (例 2.9 “Validator.validate() 使用方法” ).
例 2.9. Validator.validate()
使用方法
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Car car = new Car(null);
Set<ConstraintViolation<Car>> constraintViolations = validator.validate(car);
assertEquals(1, constraintViolations.size());
assertEquals("may not be null", constraintViolations.iterator().next().getMessage());
通过validateProperty()
可以对一个给定实体对象的单个属性进行校验. 其中属性名称需要符合JavaBean规范中定义的属性名称.
例 2.10. Validator.validateProperty()
使用方法
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Car car = new Car(null);
Set<ConstraintViolation<Car>> constraintViolations = validator.validateProperty(car, "manufacturer");
assertEquals(1, constraintViolations.size());
assertEquals("may not be null", constraintViolations.iterator().next().getMessage());
例如, Validator.validateProperty
可以被用在把Bean Validation集成进JSF 2中的时候使用 (请参考 第 7.4 节 “展示层校验”).
通过validateValue()
方法,你能够校验如果把一个特定的值赋给一个类的某一个属性的话,是否会违反此类中定义的约束条件.
例 2.11. Validator.validateValue()
使用方法
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<Car>> constraintViolations = validator.validateValue(Car.class, "manufacturer", null);
assertEquals(1, constraintViolations.size());
assertEquals("may not be null", constraintViolations.iterator().next().getMessage());
validateProperty()
和 validateValue()
会忽略被验证属性上定义的@Valid
.
现在是时候看看究竟ConstraintViolation
是什么了. ConstraintViolation
中包含了很多方法能够帮你快速定位究竟是什么导致了校验失败.表 2.1 “ConstraintViolation 中的方法” 列出了这些方法:
表 2.1. ConstraintViolation
中的方法
方法名 | 作用 | 示例 (请参考例 2.9 “Validator.validate() 使用方法”) |
---|---|---|
getMessage() | 获取(经过翻译的)校验错误信息 | may not be null |
getMessageTemplate() | 获取错误信息模版 | {javax.validation.constraints.NotNull.message} |
getRootBean() | 获取被校验的根实体对象 | car |
getRootBeanClass() | 获取被校验的根实体类. | Car.class |
getLeafBean() | 如果约束是添加在一个bean(实体对象)上的,那么则返回这个bean的实例, 如果是约束是定义在一个属性上的, 则返回这个属性所属的bean的实例对象. | car |
getPropertyPath() | 从被验证的根对象到被验证的属性的路径. | |
getInvalidValue() | 倒是校验失败的值. | passengers |
getConstraintDescriptor() | 导致校验失败的约束定义. |
在第 3 章 创建自己的约束规则中,我们会看到,每个约束定义中都包含有一个用于提示验证结果的消息模版, 并且在声明一个约束条件的时候,你可以通过这个约束中的message
属性来重写默认的消息模版, 可以参考例 2.13 “Driver”. 如果在校验的时候,这个约束条件没有通过,那么你配置的MessageInterpolator
会被用来当成解析器来解析这个约束中定义的消息模版, 从而得到最终的验证失败提示信息. 这个解析器会尝试解析模版中的占位符( 大括号括起来的字符串 ). 其中, Hibernate Validator中默认的解析器 (MessageInterpolator
) 会先在类路径下找名称为ValidationMessages.properties
的ResourceBundle
, 然后将占位符和这个文件中定义的resource进行匹配,如果匹配不成功的话,那么它会继续匹配Hibernate Validator自带的位于/org/hibernate/validator/ValidationMessages.properties
的ResourceBundle
, 依次类推,递归的匹配所有的占位符.
因为大括号{ 在这里是特殊字符,所以,你可以通过使用反斜线来对其进行转义, 例如:
\{ 被转义成 {
\} 被转义成 }
\\ 被转义成 \
如果默认的消息解析器不能够满足你的需求,那么你也可以在创建ValidatorFactory
的时候, 将其替换为一个你自定义的MessageInterpolator
, 具体请参考 第 5 章 Bootstrapping.
校验组能够让你在验证的时候选择应用哪些约束条件. 这样在某些情况下( 例如向导 ) 就可以对每一步进行校验的时候, 选取对应这步的那些约束条件进行验证了. 校验组是通过可变参数传递给validate
, validateProperty
和 validateValue
的. 让我们来看个例子, 这个实例扩展了上面的Car
类,又为其添加了一个新的Driver
. 首先, 类Person
(例 2.12 “Person”) 的name属性上定义了一个@NotNull
的约束条件. 因为没有明确指定这个约束条件属于哪个组,所以它被归类到默认组 (javax.validation.groups.Default
).
如果某个约束条件属于多个组,那么各个组在校验时候的顺序是不可预知的. 如果一个约束条件没有被指明属于哪个组,那么它就会被归类到默认组(javax.validation.groups.Default
).
例 2.12. Person
public class Person {
@NotNull
private String name;
public Person(String name) {
this.name = name;
}
// getters and setters ...
}
接下来, 我们让类Driver
(例 2.13 “Driver”) 继承自类Person
. 然后添加两个属性,分别是age 和 hasDrivingLicense. 对于一个司机来说, 要开车的话, 必须满足两个条件, 年满18周岁 (@Min(18)
) 和你的有驾照(@AssertTrue
). 这两个约束条件分别定义在那两个属性上, 并且把他们都归于DriverChecks
组. 通过例 2.14 “Group interfaces”, 你可以看到, "DriverChecks
组" 就是一个简单的标记接口. 使用接口( 而不是字符串) 可以做到类型安全,并且接口比字符串更加对重构友好, 另外, 接口还意味着一个组可以继承别的组.
例 2.13. Driver
public class Driver extends Person {
@Min(value = 18, message = "You have to be 18 to drive a car", groups = DriverChecks.class)
public int age;
@AssertTrue(message = "You first have to pass the driving test", groups = DriverChecks.class)
public boolean hasDrivingLicense;
public Driver(String name) {
super( name );
}
public void passedDrivingTest(boolean b) {
hasDrivingLicense = b;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
最后, 我们给Car
class (例 2.15 “Car”) 添加一个passedVehicleInspection的属性,来表示这个车是否通过了上路检查.
例 2.15. Car
public class Car {
@NotNull
private String manufacturer;
@NotNull
@Size(min = 2, max = 14)
private String licensePlate;
@Min(2)
private int seatCount;
@AssertTrue(message = "The car has to pass the vehicle inspection first", groups = CarChecks.class)
private boolean passedVehicleInspection;
@Valid
private Driver driver;
public Car(String manufacturer, String licencePlate, int seatCount) {
this.manufacturer = manufacturer;
this.licensePlate = licencePlate;
this.seatCount = seatCount;
}
}
现在, 在我们的例子中有三个不同的校验组, Person.name, Car.manufacturer, Car.licensePlate 和 Car.seatCount都属于默认(Default
) 组, Driver.age 和 Driver.hasDrivingLicense 从属于 DriverChecks
组, 而Car.passedVehicleInspection 在CarChecks
组中. 例 2.16 “Drive away”演示了如何让Validator.validate
验证不同的组来得到不同的校验结果.
例 2.16. Drive away
public class GroupTest {
private static Validator validator;
@BeforeClass
public static void setUp() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
@Test
public void driveAway() {
// create a car and check that everything is ok with it.
Car car = new Car( "Morris", "DD-AB-123", 2 );
Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car );
assertEquals( 0, constraintViolations.size() );
// but has it passed the vehicle inspection?
constraintViolations = validator.validate( car, CarChecks.class );
assertEquals( 1, constraintViolations.size() );
assertEquals("The car has to pass the vehicle inspection first", constraintViolations.iterator().next().getMessage());
// let's go to the vehicle inspection
car.setPassedVehicleInspection( true );
assertEquals( 0, validator.validate( car ).size() );
// now let's add a driver. He is 18, but has not passed the driving test yet
Driver john = new Driver( "John Doe" );
john.setAge( 18 );
car.setDriver( john );
constraintViolations = validator.validate( car, DriverChecks.class );
assertEquals( 1, constraintViolations.size() );
assertEquals( "You first have to pass the driving test", constraintViolations.iterator().next().getMessage() );
// ok, John passes the test
john.passedDrivingTest( true );
assertEquals( 0, validator.validate( car, DriverChecks.class ).size() );
// just checking that everything is in order now
assertEquals( 0, validator.validate( car, Default.class, CarChecks.class, DriverChecks.class ).size() );
}
}
首先我们创建一辆汽车然后在没有明确指定使用哪个校验组的情况下校验它, 可以看到即使passedVehicleInspection的默认值是false
也不会校验出错误来. 因为定义在这个属性上的约束条件并不属于默认的校验组, 接下来,我们来校验CarChecks
这个组, 这样就会发现car违反了约束条件, 必须让这个车先通过检测. 接下来,我们给这个车增加一个司机, 然后在基于DriverChecks
来校验, 会发现因为这个司机因为还没有通过驾照考试, 所以又一次得到了校验错误, 如果我们设置passedDrivingTest属性为true
之后, DriverChecks
组的校验就通过了.
最后, 让我们再来校验所有的组中定义的约束条件,可以看到所有的约束条件都通过了验证.
By default, constraints are evaluated in no particular order, regardless of which groups they belong to. In some situations, however, it is useful to control the order constraints are evaluated. In our example from 第 2.3 节 “校验组” we could for example require that first all default car constraints are passing before we check the road worthiness of the car. Finally before we drive away we check the actual driver constraints. In order to implement such an order one would define a new interface and annotate it with @GroupSequence
defining the order in which the groups have to be validated.
If at least one constraint fails in a sequenced group none of the constraints of the following groups in the sequence get validated.
例 2.17. 标注了@GroupSequence的接口
@GroupSequence({Default.class, CarChecks.class, DriverChecks.class})
public interface OrderedChecks {
}
一个校验组序列中包含的校验组和这个校验组序列不能造成直接或者间接的循环引用. 包括校验组继承. 如果造成了循环引用的话, 会导致GroupDefinitionException
异常.
例 2.18 “校验组序列的用法”展示了校验组序列的用法.
例 2.18. 校验组序列的用法
@Test
public void testOrderedChecks() {
Car car = new Car( "Morris", "DD-AB-123", 2 );
car.setPassedVehicleInspection( true );
Driver john = new Driver( "John Doe" );
john.setAge( 18 );
john.passedDrivingTest( true );
car.setDriver( john );
assertEquals( 0, validator.validate( car, OrderedChecks.class ).size() );
}
The @GroupSequence
annotation also fulfills a second purpose. It allows you to redefine what the Default
group means for a given class. To redefine Default
for a given class, add a @GroupSequence
annotation to the class. The defined groups in the annotation express the sequence of groups that substitute Default
for this class. 例 2.19 “RentalCar with @GroupSequence” introduces a new class RentalCar
with a redefined default group. With this definition you can evaluate the constraints belonging to RentalChecks
, CarChecks
and RentalCar
by just requesting the Default
group as seen in 例 2.20 “RentalCar with redefined default group”.
例 2.19. RentalCar with @GroupSequence
@GroupSequence({ RentalChecks.class, CarChecks.class, RentalCar.class })
public class RentalCar extends Car {
@AssertFalse(message = "The car is currently rented out", groups = RentalChecks.class)
private boolean rented;
public RentalCar(String manufacturer, String licencePlate, int seatCount) {
super( manufacturer, licencePlate, seatCount );
}
public boolean isRented() {
return rented;
}
public void setRented(boolean rented) {
this.rented = rented;
}
}
例 2.20. RentalCar with redefined default group
/**
* Validating the default group leads to validation on the default group sequence of {@code RentalCar}.
*/
@Test
public void carIsRented() {
RentalCar rentalCar = new RentalCar( "Morris", "DD-AB-123", 2 );
rentalCar.setPassedVehicleInspection( true );
rentalCar.setRented( true );
Set<ConstraintViolation<RentalCar>> constraintViolations = validator.validate( rentalCar );
assertEquals( 1, constraintViolations.size() );
assertEquals(
"Wrong message",
"The car is currently rented out",
constraintViolations.iterator().next().getMessage()
);
rentalCar.setRented( false );
constraintViolations = validator.validate( rentalCar );
assertEquals( 0, constraintViolations.size() );
}
Due to the fact that there cannot be a cyclic dependency in the group and group sequence definitions one cannot just add Default
to the sequence redefining Default
for a class. Instead the class itself has to be added!
The Default
group sequence overriding is local to the class it is defined on and is not propagated to the associated objects. This means in particular that adding DriverChecks
to the default group sequence of RentalCar
would not have any effects. Only the group Default
will be propagated to the driver association when validation a rental car instance.
The @javax.validation.GroupSequence
annotation is a standardized Bean Validation annotation. As seen in the previous section it allows you to statically redefine the default group sequence for a class. Hibernate Validator also offers a custom, non standardized annotation - org.hibernate.validator.group.GroupSequenceProvider
- which allows for dynamic redefinition of the default group sequence. Using the rental car scenario again, one could dynamically add the CarChecks
as seen in 例 2.21 “RentalCar with @GroupSequenceProvider” and 例 “DefaultGroupSequenceProvider implementation”.
例 2.21. RentalCar with @GroupSequenceProvider
@GroupSequenceProvider(RentalCarGroupSequenceProvider.class)
public class RentalCar extends Car {
@AssertFalse(message = "The car is currently rented out", groups = RentalChecks.class)
private boolean rented;
public RentalCar(String manufacturer, String licencePlate, int seatCount) {
super( manufacturer, licencePlate, seatCount );
}
public boolean isRented() {
return rented;
}
public void setRented(boolean rented) {
this.rented = rented;
}
}
例 . DefaultGroupSequenceProvider implementation
public class RentalCarGroupSequenceProvider implements DefaultGroupSequenceProvider<RentalCar> {
public List<Class<?>> getValidationGroups(RentalCar car) {
List<Class<?>> defaultGroupSequence = new ArrayList<Class<?>>();
defaultGroupSequence.add( RentalCar.class );
if ( car != null && !car.isRented() ) {
defaultGroupSequence.add( CarChecks.class );
}
return defaultGroupSequence;
}
}
Hibernate Validator comprises a basic set of commonly used constraints. These are foremost the constraints defined by the Bean Validation specification (see 表 2.2 “Bean Validation constraints”). Additionally, Hibernate Validator provides useful custom constraints (see 表 2.3 “Custom constraints” and 表 2.4 “Custom country specific constraints”).
表 2.2 “Bean Validation constraints” shows purpose and supported data types of all constraints specified in the Bean Validation API. All these constraints apply to the field/property level, there are no class-level constraints defined in the Bean Validation specification. If you are using the Hibernate object-relational mapper, some of the constraints are taken into account when creating the DDL for your model (see column "Hibernate metadata impact").
Hibernate Validator allows some constraints to be applied to more data types than required by the Bean Validation specification (e.g. @Max can be applied to Strings
). Relying on this feature can impact portability of your application between Bean Validation providers.
表 2.2. Bean Validation constraints
Annotation | Supported data types | 作用 | Hibernate metadata impact |
---|---|---|---|
@AssertFalse | Boolean , boolean | Checks that the annotated element is false . | 没有 |
@AssertTrue | Boolean , boolean | Checks that the annotated element is true . | 没有 |
@DecimalMax | BigDecimal , BigInteger , String , byte , short , int , long and the respective wrappers of the primitive types. Additionally supported by HV: any sub-type of Number and CharSequence . | 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal 定义的最大值的字符串表示. | 没有 |
@DecimalMin | BigDecimal , BigInteger , String , byte , short , int , long and the respective wrappers of the primitive types. Additionally supported by HV: any sub-type of Number and CharSequence . | 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal 定义的最小值的字符串表示. | 没有 |
@Digits(integer=, fraction=) | BigDecimal , BigInteger , String , byte , short , int , long and the respective wrappers of the primitive types. Additionally supported by HV: any sub-type of Number and CharSequence . | Checks whether the annoted value is a number having up to integer digits and fraction fractional digits. | 对应的数据库表字段会被设置精度(precision)和准度(scale). |
@Future | java.util.Date , java.util.Calendar ; Additionally supported by HV, if the Joda Time date/time API is on the class path: any implementations of ReadablePartial and ReadableInstant . | 检查给定的日期是否比现在晚. | 没有 |
@Max | BigDecimal , BigInteger , byte , short , int , long and the respective wrappers of the primitive types. Additionally supported by HV: any sub-type ofCharSequence (the numeric value represented by the character sequence is evaluated), any sub-type of Number . | 检查该值是否小于或等于约束条件中指定的最大值. | 会给对应的数据库表字段添加一个check的约束条件. |
@Min | BigDecimal , BigInteger , byte , short , int , long and the respective wrappers of the primitive types. Additionally supported by HV: any sub-type of CharSequence (the numeric value represented by the char sequence is evaluated), any sub-type of Number . | 检查该值是否大于或等于约束条件中规定的最小值. | 会给对应的数据库表字段添加一个check的约束条件. |
@NotNull | Any type | Checks that the annotated value is not null. | 对应的表字段不允许为null. |
@Null | Any type | Checks that the annotated value is null. | 没有 |
@Past | java.util.Date , java.util.Calendar ; Additionally supported by HV, if the Joda Time date/time API is on the class path: any implementations of ReadablePartial and ReadableInstant . | 检查标注对象中的值表示的日期比当前早. | 没有 |
@Pattern(regex=, flag=) | String . Additionally supported by HV: any sub-type of CharSequence . | 检查该字符串是否能够在match 指定的情况下被regex 定义的正则表达式匹配. | 没有 |
@Size(min=, max=) | String , Collection , Map and arrays . Additionally supported by HV: any sub-type of CharSequence . | Checks if the annotated element's size is between min and max (inclusive). | 对应的数据库表字段的长度会被设置成约束中定义的最大值. |
@Valid | Any non-primitive type | 递归的对关联对象进行校验, 如果关联对象是个集合或者数组, 那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验. | 没有 |
On top of the parameters indicated in 表 2.2 “Bean Validation constraints” each constraint supports the parameters message
, groups
and payload
. This is a requirement of the Bean Validation specification.
In addition to the constraints defined by the Bean Validation API Hibernate Validator provides several useful custom constraints which are listed in 表 2.3 “Custom constraints”. With one exception also these constraints apply to the field/property level, only @ScriptAssert is a class-level constraint.
表 2.3. Custom constraints
Annotation | Supported data types | 作用 | Hibernate metadata impact |
---|---|---|---|
@CreditCardNumber | CharSequence | Checks that the annotated character sequence passes the Luhn checksum test. Note, this validation aims to check for user mistakes, not credit card validity! See also Anatomy of Credit Card Numbers. | 没有 |
CharSequence | Checks whether the specified character sequence is a valid email address. The optional parameters regexp and flags allow to specify an additional regular expression (including regular expression flags) which the email must match. | 没有 | |
@Length(min=, max=) | CharSequence | Validates that the annotated character sequence is between min and max included. | 对应的数据库表字段的长度会被设置成约束中定义的最大值. |
@ModCheck(modType=, multiplier=, startIndex=, endIndex=, checkDigitPosition=, ignoreNonDigitCharacters=) | CharSequence | Checks that the digits within the annotated character sequence pass the mod 10 or mod 11 checksum algorithm. modType is used to select the modulo type and the multiplier determines the algorithm specific multiplier (see also Luhn algorithm). startIndex and endIndex allow to only run the modulo algorithm on the specified sub-string. checkDigitPosition allows to use an arbitrary digit within the character sequence to be the check digit. If not specified it is assumed that the check digit is part of the specified range. Last but not least, ignoreNonDigitCharacters allows to ignore non digit characters. | 没有 |
@NotBlank | CharSequence | Checks that the annotated character sequence is not null and the trimmed length is greater than 0. The difference to @NotEmpty is that this constraint can only be applied on strings and that trailing whitespaces are ignored. | 没有 |
@NotEmpty | CharSequence , Collection , Map and arrays | Checks whether the annotated element is not null nor empty. | 没有 |
@Range(min=, max=) | BigDecimal , BigInteger , CharSequence , byte , short , int , long and the respective wrappers of the primitive types | Checks whether the annotated value lies between (inclusive) the specified minimum and maximum. | 没有 |
@SafeHtml(whitelistType=, additionalTags=) | CharSequence | Checks whether the annotated value contains potentially malicious fragments such as <script/> . In order to use this constraint, the jsoup library must be part of the class path. With the whitelistType attribute predefined whitelist types can be chosen. You can also specify additional html tags for the whitelist with the additionalTags attribute. | 没有 |
@ScriptAssert(lang=, script=, alias=) | Any type | Checks whether the given script can successfully be evaluated against the annotated element. In order to use this constraint, an implementation of the Java Scripting API as defined by JSR 223 ("Scripting for the JavaTM Platform") must part of the class path. The expressions to be evaluated can be written in any scripting or expression language, for which a JSR 223 compatible engine can be found in the class path. | 没有 |
@URL(protocol=, host=, port=, regexp=, flags=) | CharSequence | Checks if the annotated character sequence is a valid URL according to RFC2396. If any of the optional parameters protocol , host or port are specified, the corresponding URL fragments must match the specified values. The optional parameters regexp and flags allow to specify an additional regular expression (including regular expression flags) which the URL must match. | 没有 |
Hibernate Validator offers also some country specific constraints, e.g. for the validation of social security numbers.
If you have to implement a country specific constraint, consider making it a contribution to Hibernate Validator!
表 2.4. Custom country specific constraints
Annotation | Supported data types | 作用 | Country | Hibernate metadata impact |
---|---|---|---|---|
@CNPJ | CharSequence | Checks that the annotated character sequence represents a Brazilian corporate tax payer registry number (Cadastro de Pessoa Juríeddica) | Brazil | 没有 |
@CPF | CharSequence | Checks that the annotated character sequence represents a Brazilian individual taxpayer registry number (Cadastro de Pessoa Fídsica). | Brazil | 没有 |
@TituloEleitoral | CharSequence | Checks that the annotated character sequence represents a Brazilian voter ID card number (Título Eleitoral). | Brazil | 没有 |
In some cases neither the Bean Validation constraints nor the custom constraints provided by Hibernate Validator will fulfill your requirements. In this case you can easily write your own constraint. We will discuss this in 第 3 章 创建自己的约束规则.
版权 © 2009 - 2011 Red Hat, Inc. & Gunnar Morling