Hibernate Validator
JSR 303 的参考实现
使用指南
4.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 theApache 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 theMaven 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 callingConfiguration.ignoreXmlConfiguration()
duringValidationFactory
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 commandmvn 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 atMaven Archetype pluginpage.
1.2.添加约束
在你喜欢的IDE中打开这个项目中的Car
类:
例1.3.带约束性标注(annotated with constraints)的Car 类
packagecom.mycompany;importjavax.validation.constraints.Min;importjavax.validation.constraints.NotNull;importjavax.validation.constraints.Size;publicclassCar{@NotNullprivateStringmanufacturer;@NotNull@Size(min=2,max=14)privateStringlicensePlate;@Min(2)privateintseatCount;publicCar(Stringmanufacturer,StringlicencePlate,intseatCount){this.manufacturer=manufacturer;this.licensePlate=licencePlate;this.seatCount=seatCount;}//gettersandsetters...}
@NotNull
,@Size
and@Min
就是上面所属的约束性标注( constraint annotations), 我们就是使用它们来声明约束, 例如在Car
的字段中我们可以看到:
manufacturer永远不能为null
licensePlate永远不能为null,并且它的值字符串的长度要在2到14之间
seatCount的值要不能小于2
1.3.校验约束
我们需要使用Validator
来对上面的那些约束进行校验. 让我们来看看CarTest
这个类:
例1.4.在CarTest中使用校验
packagecom.mycompany;importstaticorg.junit.Assert.*;importjava.util.Set;importjavax.validation.ConstraintViolation;importjavax.validation.Validation;importjavax.validation.Validator;importjavax.validation.ValidatorFactory;importorg.junit.BeforeClass;importorg.junit.Test;publicclassCarTest{privatestaticValidatorvalidator;@BeforeClasspublicstaticvoidsetUp(){ValidatorFactoryfactory=Validation.buildDefaultValidatorFactory();validator=factory.getValidator();}@TestpublicvoidmanufacturerIsNull(){Carcar=newCar(null,"DD-AB-123",4);Set<ConstraintViolation<Car>>constraintViolations=validator.validate(car);assertEquals(1,constraintViolations.size());assertEquals("maynotbenull",constraintViolations.iterator().next().getMessage());}@TestpublicvoidlicensePlateTooShort(){Carcar=newCar("Morris","D",4);Set<ConstraintViolation<Car>>constraintViolations=validator.validate(car);assertEquals(1,constraintViolations.size());assertEquals("sizemustbebetween2and14",constraintViolations.iterator().next().getMessage());}@TestpublicvoidseatCountTooLow(){Carcar=newCar("Morris","DD-AB-123",1);Set<ConstraintViolation<Car>>constraintViolations=validator.validate(car);assertEquals(1,constraintViolations.size());assertEquals("mustbegreaterthanorequalto2",constraintViolations.iterator().next().getMessage());}@TestpublicvoidcarIsValid(){Carcar=newCar("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中的packagejavax.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) 约束
packagecom.mycompany;importjavax.validation.constraints.NotNull;publicclassCar{@NotNullprivateStringmanufacturer;@AssertTrueprivatebooleanisRegistered;publicCar(Stringmanufacturer,booleanisRegistered){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.属性级约束
packagecom.mycompany;importjavax.validation.constraints.AssertTrue;importjavax.validation.constraints.NotNull;publicclassCar{privateStringmanufacturer;privatebooleanisRegistered;publicCar(Stringmanufacturer,booleanisRegistered){super();this.manufacturer=manufacturer;this.isRegistered=isRegistered;}@NotNullpublicStringgetManufacturer(){returnmanufacturer;}publicvoidsetManufacturer(Stringmanufacturer){this.manufacturer=manufacturer;}@AssertTruepublicbooleanisRegistered(){returnisRegistered;}publicvoidsetRegistered(booleanisRegistered){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 fieldorproperty annotations within one class. It is not recommended to annotate a fieldandthe 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.类级别约束
packagecom.mycompany;importjavax.validation.constraints.Min;importjavax.validation.constraints.NotNull;importjavax.validation.constraints.Size;@PassengerCountpublicclassCar{@NotNullprivateStringmanufacturer;@NotNull@Size(min=2,max=14)privateStringlicensePlate;@Min(2)privateintseatCount;privateList<Person>passengers;publicCar(Stringmanufacturer,StringlicencePlate,intseatCount){this.manufacturer=manufacturer;this.licensePlate=licencePlate;this.seatCount=seatCount;}//gettersandsetters...}
2.1.4.约束继承
如果要验证的对象继承于某个父类或者实现了某个接口,那么定义在父类或者接口中的约束会在验证这个对象的时候被自动加载,如同这些约束定义在这个对象所在的类中一样. 让我们来看看下面的示例:
例2.4.约束继承
packagecom.mycompany;importjavax.validation.constraints.NotNull;publicclassRentalCarextendsCar{privateStringrentalStation;publicRentalCar(Stringmanufacturer,StringrentalStation){super(manufacturer);this.rentalStation=rentalStation;}@NotNullpublicStringgetRentalStation(){returnrentalStation;}publicvoidsetRentalStation(StringrentalStation){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
packagecom.mycompany;importjavax.validation.constraints.NotNull;publicclassPerson{@NotNullprivateStringname;publicPerson(Stringname){super();this.name=name;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}}
例2.6.Adding a driver to the car
packagecom.mycompany;importjavax.validation.Valid;importjavax.validation.constraints.NotNull;publicclassCar{@NotNull@ValidprivatePersondriver;publicCar(Persondriver){this.driver=driver;}//gettersandsetters...}
如果校验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
packagecom.mycompany;importjava.util.ArrayList;importjava.util.List;importjavax.validation.Valid;importjavax.validation.constraints.NotNull;publicclassCar{@NotNull@ValidprivateList<Person>passengers=newArrayList<Person>();publicCar(List<Person>passengers){this.passengers=passengers;}//gettersandsetters...}
当校验一个Car
的实例的时候,如果passengerslist中包含的任何一个Person
对象没有名字的话,都会导致校验失败(aConstraintValidation
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()
ValidatorFactoryfactory=Validation.buildDefaultValidatorFactory();Validatorvalidator=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()
使用方法
ValidatorFactoryfactory=Validation.buildDefaultValidatorFactory();Validatorvalidator=factory.getValidator();Carcar=newCar(null);Set<ConstraintViolation<Car>>constraintViolations=validator.validate(car);assertEquals(1,constraintViolations.size());assertEquals("maynotbenull",constraintViolations.iterator().next().getMessage());
2.2.2.2.validateProperty
通过validateProperty()
可以对一个给定实体对象的单个属性进行校验. 其中属性名称需要符合JavaBean规范中定义的属性名称.
例2.10.Validator.validateProperty()
使用方法
Validatorvalidator=Validation.buildDefaultValidatorFactory().getValidator();Carcar=newCar(null);Set<ConstraintViolation<Car>>constraintViolations=validator.validateProperty(car,"manufacturer");assertEquals(1,constraintViolations.size());assertEquals("maynotbenull",constraintViolations.iterator().next().getMessage());
例如,Validator.validateProperty
可以被用在把Bean Validation集成进JSF 2中的时候使用 (请参考第7.4节 “展示层校验”).
2.2.2.3.validateValue
通过validateValue()
方法,你能够校验如果把一个特定的值赋给一个类的某一个属性的话,是否会违反此类中定义的约束条件.
例2.11.Validator.validateValue()
使用方法
Validatorvalidator=Validation.buildDefaultValidatorFactory().getValidator();Set<ConstraintViolation<Car>>constraintViolations=validator.validateValue(Car.class,"manufacturer",null);assertEquals(1,constraintViolations.size());assertEquals("maynotbenull",constraintViolations.iterator().next().getMessage());
注意
validateProperty()
和validateValue()
会忽略被验证属性上定义的@Valid
.
知识推荐
- hibernate学习三 精解Hibernate之核心文件
- 专升本第七讲(HTML制作)
- Web服务器负载均衡的几种方案 : DNS轮询
- MVC路由机制
- machine learning 之 Neural Network 1
- 没人看系列----css 随笔
- 深度剖析PHP序列化和反序列化
- jQuery Mobile入门经典 ([美] Phil Dutson) 中文pdf扫描版
- js中如何避免动态引入重复资源
- css布局
- php curl数据传输神器
- Thinkphp3.2版本结合phpqrcode生成二维码并提供下载
- [Bzoj5179][Jsoi2011]任务调度(左偏树)
- 21天网站建设实录 (雨辰资讯) 高清pdf扫描版
- HTML的a标签无法下载文件的解决方案
- 文件上传-1
- node 基本搭建 server.js
- 前端秘籍,看程序员如何用九个招式,搞定css水平居中