Hibernate Validator
JSR 303 的参考实现
使用指南
由 Hardy Ferentschik和Gunnar Morling and thanks to Shaozhuang Liu4.2.0.Final
版权 © 2009 - 2011 Red Hat, Inc. & Gunnar Morling
June 20, 2011
- 序言
- 1. 开始入门
-
- 1.1. 第一个Maven项目
- 1.2. 添加约束
- 1.3. 校验约束
- 1.4. 更进一步
- 2. Validation step by step
-
- 2.1. 定义约束
-
- 2.1.1. 字段级(field level) 约束
- 2.1.2. 属性级别约束
- 2.1.3. 类级别约束
- 2.1.4. 约束继承
- 2.1.5. 对象图
- 2.2. 校验约束
-
- 2.2.1. 获取一个Validator的实例
- 2.2.2. Validator中的方法
- 2.2.3. ConstraintViolation 中的方法
- 2.2.4. 验证失败提示信息解析
- 2.3. 校验组
-
- 2.3.1. 校验组序列
- 2.3.2. 对一个类重定义其默认校验组
- 2.4. 内置的约束条件
-
- 2.4.1. Bean Validation constraints
- 2.4.2. Additional constraints
- 3. 创建自己的约束规则
-
- 3.1. 创建一个简单的约束条件
-
- 3.1.1. 约束标注
- 3.1.2. 约束校验器
- 3.1.3. 校验错误信息
- 3.1.4. 应用约束条件
- 3.2. 约束条件组合
- 4. XML configuration
-
- 4.1. validation.xml
- 4.2. 映射约束
- 5. Bootstrapping
-
- 5.1. Configuration 和 ValidatorFactory
- 5.2. ValidationProviderResolver
- 5.3. MessageInterpolator
-
- 5.3.1. ResourceBundleLocator
- 5.4. TraversableResolver
- 5.5. ConstraintValidatorFactory
- 6. Metadata API
-
- 6.1. BeanDescriptor
- 6.2. PropertyDescriptor
- 6.3. ElementDescriptor
- 6.4. ConstraintDescriptor
- 7. 与其他框架集成
-
- 7.1. OSGi
- 7.2. 与数据库集成校验
- 7.3. ORM集成
-
- 7.3.1. 基于Hibernate事件模型的校验
- 7.3.2. JPA
- 7.4. 展示层校验
- 8. Hibernate Validator Specifics
-
- 8.1. Public API
- 8.2. Fail fast mode
- 8.3. Method validation
-
- 8.3.1. Defining method-level constraints
- 8.3.2. Evaluating method-level constraints
- 8.3.3. Retrieving method-level constraint meta data
- 8.4. Programmatic constraint definition
- 8.5. Boolean composition for constraint composition
- 9. Annotation Processor
-
- 9.1. 前提条件
- 9.2. 特性
- 9.3. 配置项
- 9.4. 使用标注处理器
-
- 9.4.1. 命令行编译
- 9.4.2. IDE集成
- 9.5. 已知问题
- 10. 进一步阅读
序言
数据校验是任何一个应用程序都会用到的功能,无论是显示层还是持久层. 通常,相同的校验逻辑会分散在各个层中, 这样,不仅浪费了时间还会导致错误的发生(译注: 重复代码). 为了避免重复, 开发人员经常会把这些校验逻辑直接写在领域模型里面, 但是这样又把领域模型代码和校验代码混杂在了一起, 而这些校验逻辑更应该是描述领域模型的元数据.

JSR 303 - Bean Validation - 为实体验证定义了元数据模型和API. 默认的元数据模型是通过Annotations来描述的,但是也可以使用XML来重载或者扩展. Bean Validation API 并不局限于应用程序的某一层或者哪种编程模型, 例如,如图所示, Bean Validation 可以被用在任何一层, 或者是像类似Swing的富客户端程序中.

Hibernate Validator is the reference implementation of this JSR. The implementation itself as well as the Bean Validation API and TCK are all provided and distributed under the Apache Software License 2.0.
第 1 章 开始入门
- 1.1. 第一个Maven项目
- 1.2. 添加约束
- 1.3. 校验约束
- 1.4. 更进一步
本章将会告诉你如何使用Hibernate Validator, 在开始之前,你需要准备好下面的环境:
-
A JDK >= 5
-
Apache Maven
-
网络连接 ( Maven需要通过互联网下载所需的类库)
-
A properly configured remote repository. Add the following to your
settings.xml
:例 1.1. Configuring the JBoss Maven repository
<repositories> <repository> <id>jboss-public-repository-group</id> <url>https://repository.jboss.org/nexus/content/groups/public-jboss</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories>
More information aboutsettings.xml
can be found in the Maven Local Settings Model.
注意
Hibernate Validator uses JAXB for XML parsing. JAXB is part of the Java Class Library since Java 6 which means that if you run Hibernate Validator with Java 5 you will have to add additional JAXB dependencies. Using Maven you have to add the following dependencies:
<dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.1.12</version> </dependency>
if you are using the SourceForge package you find the necessary libraries in thelib/jdk5
directory.
In case you are not using the XML configuration you can also disable it explicitly by calling Configuration.ignoreXmlConfiguration()
during ValidationFactory
creation.
In this case the JAXB dependencies are not needed.
1.1. 第一个Maven项目
使用Maven archetype插件来创建一个新的Maven 项目
例 1.2. 使用Maven archetype 插件来创建一个简单的基于Hibernate Validator的项目
mvn archetype:generate -DarchetypeGroupId=org.hibernate -DarchetypeArtifactId=hibernate-validator-quickstart-archetype -DarchetypeVersion=4.2.0.Final -DarchetypeRepository=http://repository.jboss.org/nexus/content/groups/public-jboss/ -DgroupId=com.mycompany -DartifactId=hv-quickstart
Maven 将会把你的项目创建在hv-quickstart目录中. 进入这个目录并且执行:
mvn test
这样, Maven会编译示例代码并且运行单元测试, 接下来,让我们看看生成的代码.
注意
From version 4.2.0.Beta2, the maven command mvn archetype:create
will
be no longer supported and will fail. You should use the command described in the above listing. If you want more details, look at Maven
Archetype plugin page.
1.2. 添加约束
在你喜欢的IDE中打开这个项目中的Car
类:
例 1.3. 带约束性标注(annotated with constraints)的Car 类
package com.mycompany; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; public class Car { @NotNull private String manufacturer; @NotNull @Size(min = 2, max = 14) private String licensePlate; @Min(2) private int seatCount; public Car(String manufacturer, String licencePlate, int seatCount) { this.manufacturer = manufacturer; this.licensePlate = licencePlate; this.seatCount = seatCount; } //getters and setters ... }
@NotNull
, @Size
and @Min
就是上面所属的约束性标注(
constraint annotations), 我们就是使用它们来声明约束, 例如在Car
的字段中我们可以看到:
-
manufacturer永远不能为null
-
licensePlate永远不能为null,并且它的值字符串的长度要在2到14之间
-
seatCount的值要不能小于2
1.3. 校验约束
我们需要使用Validator
来对上面的那些约束进行校验. 让我们来看看CarTest
这个类:
例 1.4. 在CarTest中使用校验
package com.mycompany; import static org.junit.Assert.*; import java.util.Set; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; import org.junit.BeforeClass; import org.junit.Test; public class CarTest { private static Validator validator; @BeforeClass public static void setUp() { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); validator = factory.getValidator(); } @Test public void manufacturerIsNull() { Car car = new Car(null, "DD-AB-123", 4); Set<ConstraintViolation<Car>> constraintViolations = validator.validate(car); assertEquals(1, constraintViolations.size()); assertEquals("may not be null", constraintViolations.iterator().next().getMessage()); } @Test public void licensePlateTooShort() { Car car = new Car("Morris", "D", 4); Set<ConstraintViolation<Car>> constraintViolations = validator.validate(car); assertEquals(1, constraintViolations.size()); assertEquals("size must be between 2 and 14", constraintViolations.iterator().next().getMessage()); } @Test public void seatCountTooLow() { Car car = new Car("Morris", "DD-AB-123", 1); Set<ConstraintViolation<Car>> constraintViolations = validator.validate(car); assertEquals(1, constraintViolations.size()); assertEquals("must be greater than or equal to 2", constraintViolations.iterator().next().getMessage()); } @Test public void carIsValid() { Car car = new Car("Morris", "DD-AB-123", 2); Set<ConstraintViolation<Car>> constraintViolations = validator.validate(car); assertEquals(0, constraintViolations.size()); } }
在setUp()
方法中,我们通过ValidatorFactory
得到了一个Validator
的实例. Validator
是线程安全的,并且可以重复使用,
所以我们把它保存成一个类变量. 现在我们可以在test方法中使用这个validator的实例来校验不同的car实例了.
validate()
方法会返回一个set的ConstraintViolation
的实例的集合,
我们可以通过遍历它来查看有哪些验证错误. 前面三个测试用例显示了一些预期的校验约束:
-
在
manufacturerIsNull()
中可以看到manufacturer违反了@NotNull
约束 -
licensePlateTooShort()
中的licensePlate违反了@Size
约束 -
而
seatCountTooLow()
中则导致seatCount违反了@Min
约束
如果一个对象没有校验出问题的话,那么validate()
会返回一个空的set对象.
注意,我们只使用了Bean Validation API中的package javax.validation中的类, 并没有直接调用参考实现中的任何类,所以, 没有任何问题如果切换到其他的实现.
1.4. 更进一步
That concludes our 5 minute tour through the world of Hibernate Validator. Continue exploring the code examples or look at further examples referenced in 第 10 章 进一步阅读. To deepen your understanding of Hibernate Validator just continue reading 第 2 章 Validation step by step. In case your application has specific validation requirements have a look at 第 3 章 创建自己的约束规则.
第 2 章 Validation step by step
- 2.1. 定义约束
-
- 2.1.1. 字段级(field level) 约束
- 2.1.2. 属性级别约束
- 2.1.3. 类级别约束
- 2.1.4. 约束继承
- 2.1.5. 对象图
- 2.2. 校验约束
-
- 2.2.1. 获取一个Validator的实例
- 2.2.2. Validator中的方法
- 2.2.3. ConstraintViolation 中的方法
- 2.2.4. 验证失败提示信息解析
- 2.3. 校验组
-
- 2.3.1. 校验组序列
- 2.3.2. 对一个类重定义其默认校验组
- 2.4. 内置的约束条件
-
- 2.4.1. Bean Validation constraints
- 2.4.2. Additional constraints
在本章中,我们会详细的介绍如何使用Hibernate Validator 来对一个给定的实体模型进行验证. 还会介绍Bean Validation规范提供了哪些默认的约束条件和Hibernate Validator提供了哪些额外的. 让我们先从如何给一个实体添加约束开始.
2.1. 定义约束
Bean Validation 的约束是通过Java 注解(annotations)来标注的. 在本节中,我们会介绍如何使用这些注解(annotations)来标注一个实体模型. 并且,我们会区分三种不通的注解(annotations) 类型.
注意
不是所有的约束都能够被用在所有的类结构上. 事实上, 没有任何定义在Bean Validation规范中的约束可以被用在class上. 约束定义中的java.lang.annotation.Target
属性定义了这个约束能够被使用在哪个层次结构上.
详细信息请参考第 3 章 创建自己的约束规则.
2.1.1. 字段级(field level) 约束
约束条件能够被标注在类的字段上面, 请参考示例例 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) 对此没有影响.
注意
静态字段或者属性是不会被校验的.
2.1.2. 属性级别约束
如果你的模型遵循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.
2.1.3. 类级别约束
最后, 一个约束也能够被放在类级别上. 当一个约束被标注在一个类上的时候,这个类的实例对象被传递给ConstraintValidator
. 当需要同时校验多个属性来验证一个对象或者一个属性在验证的时候需要另外的属性的信息的时候,
类级别的约束会很有用. 在例 2.3 “类级别约束”中, 我们给类Car
添加了一个passengers的属性.
并且我们还标注了一个PassengerCount
约束在类级别上. 稍后会看到我们是如何创建这个自定义的约束的(第 3 章 创建自己的约束规则).
现在,我们可以知道,PassengerCount
会保证这个车里乘客的数量不会超过它的座位数.
例 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.1.4. 约束继承
如果要验证的对象继承于某个父类或者实现了某个接口,那么定义在父类或者接口中的约束会在验证这个对象的时候被自动加载,如同这些约束定义在这个对象所在的类中一样. 让我们来看看下面的示例:
例 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()
方法,
那么定义在父类的这个方法上的约束和子类这个方法上定义的约束都会被校验.
2.1.5. 对象图
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
值的.
2.2. 校验约束
Validator
是Bean Validation中最主要的接口, 我们会在第 5.1 节
“Configuration 和 ValidatorFactory”中详细介绍如何获取一个Validator
的实例, 现在先让我们来看看如何使用Validator
接口中的各个方法.
2.2.1. 获取一个Validator
的实例
对一个实体对象验证之前首先需要有个Validator
对象, 而这个对象是需要通过Validation
类和 ValidatorFactory
来创建的.
最简单的方法是调用Validation.buildDefaultValidatorFactory()
这个静态方法.
例 2.8. Validation.buildDefaultValidatorFactory()
ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); Validator validator = factory.getValidator();
第 5 章 Bootstrapping介绍了其他的获取Validator实例的方法.
现在我们的目标是学习如何使用Validator
来校验实体对象.
2.2.2. Validator中的方法
Validator
中有三个方法能够被用来校验整个实体对象或者实体对象中的属性.
这三个方法都会返回一个Set<ConstraintViolation>
对象, 如果整个验证过程没有发现问题的话,那么这个set是空的, 否则, 每个违反约束的地方都会被包装成一个ConstraintViolation
的实例然后添加到set当中.
所有的校验方法都接收零个或多个用来定义此次校验是基于哪个校验组的参数. 如果没有给出这个参数的话, 那么此次校验将会基于默认的校验组 (javax.validation.groups.Default
). 第 2.3 节
“校验组”
2.2.2.1. validate
使用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());
2.2.2.2. validateProperty
通过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 节
“展示层校验”).
2.2.2.3. validateValue
通过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
.
2.2.3. ConstraintViolation
中的方法
现在是时候看看究竟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() |
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。
copyright © 2008-2019 亿联网络 版权所有 备案号:粤ICP备14031511号-2
|