分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 教程案例

Hibernate Validator

发布时间:2023-09-06 01:49责任编辑:沈小雨关键词:Hibernate

Hibernate Validator

JSR 303 的参考实现

使用指南

由HardyFerentschik和GunnarMorling
and thanks toShaozhuangLiu

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 yoursettings.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.xmlcan 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/jdk5directory. In case you are not using the XML configuration you can also disable it explicitly by callingConfiguration.ignoreXmlConfiguration()duringValidationFactorycreation. 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:createwill 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,@Sizeand@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重写了父类CargetManufacturer()方法, 那么定义在父类的这个方法上的约束和子类这个方法上定义的约束都会被校验.

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,ListSet)

  • 实现了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对象没有名字的话,都会导致校验失败(aConstraintValidationwill 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.

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved