Hibernate Validator学习笔记

学习一款强大的运行期属性检查框架

题图:from Google

引子

用Annotations给类或者类的属性加上约束(constraint),在运行期检查属性值是很优雅的,Hibernate Validator是一个验证框架,不需要和Hibernate的其他部分绑定就可以使用。

Bean Validation

Bean Validation 为 JavaBean 验证定义了相应的元数据模型和 API。缺省的元数据是 Java Annotations,通过使用 XML 可以对原有的元数据信息进行覆盖和扩展。在应用程序中,通过使用 Bean Validation 或是自己定义的约束(constraint),例如 @NotNull, @Max,就可以确保数据模型(JavaBean)的正确性。

Hibernate Validator

Hibernate Validator是Bean Validation的参考实现。Hibernate Validator提供了JSR 303规范中所有内置 constraint的实现,除此之外还有一些附加的constraint。
可以认为,Hibernate Validator是Bean Validation的一个具体实现,并且提供了更多的特性

1. 约束级别

  1. 字段级约束
  2. 属性级约束
  3. 类级别约束
  4. 约束的继承;约束会被子类继承,在校验子类时也会对父类进行校验
  5. 对象图; @Valid注解会对关联的对象也进行校验

注意:若同时对一个字段和属性加约束,该属性会被验证两次。

代码示例1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//类级别约束,默认没有提供,此处为演示,若确实需要,应该自定义约束
@checkUser
public class User implements Serializable {
private Integer id;
//字段级约束,验证时不会调用getter方法
//groups属性参见“校验组以及校验组序列”
@NotNull(groups = "usernameGroup")
private String username;
/**
* 校验完整的对象图,验证时会对Car对象同时进行校验
* 对于集合也可以进行校验,但是会忽略对集合中null值的校验
*
* 例如:
* @Valid
* private List<Car> cars;
*
*/
@NotNull
@Valid
private Car car;
private int sex;
public Integer getId(){
return id;
}
public void setId(Integer id){
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
//属性级约束,加在getter方法上
//groups属性参见“校验组以及校验组序列”
@Max(value = 2, groups = "sexGroup")
public int getSex(){
return sex;
}
public void setSex(int sex){
this.sex = sex;
}
}

2. 校验约束

Validator接口提供三种方法校验实体对象或者对象中的属性

  • validate

    1
    2
    3
    4
    5
    Set<ConstraintViolation<User>> violations = validator.validate(user);
    Iterator it = violations.iterator();
    while(it.hasNext()) {
    System.out.println(it.next().getMessage());
    }
  • validateProperty

    1
    2
    3
    4
    5
    Set<ConstraintViolation<User>> violations = validator.validateProperty(user, "username");
    Iterator it = violations.iterator();
    while(it.hasNext()) {
    System.out.println(it.next().getMessage());
    }
  • validateValue

    1
    2
    3
    4
    5
    Set<ConstraintViolation<User>> violations = validator.validateValue(User.class, "username", null);
    Iterator it = violations.iterator();
    while(it.hasNext()) {
    System.out.println(it.next().getMessage());
    }

3. 校验组以及校验组序列

在使用约束时,可以定义其所属的校验组并定义其各组的校验顺序。hibernate validator可以通过注解形式和xml配置文件形式进行配置,出于去配置文件化的考虑,这里仅以注解形式的配置进行说明

  • 校验组
    代码示例1中所示,注解中groups属性就是给约束注解进行分组,而后可以在调用validator.validate(user, "sexGroup")时指定需要校验的组

  • 校验组序列
    默认情况下,约束执行校验的顺序是不确定的。不过我们可以自定义其校验顺序。
    需要注意的是:校验组序列中的校验组和这个校验组序列不能有循环引用,包括校验组继承,否则会抛出GroupDefinitionException
    如下代码示例2所示

代码示例2

1
2
3
4
5
6
//定义校验序列
@GroupSequence({Default.class, "sexGroup", "usernameGroup"})
public interface orderedChecks {
}
//使用指定校验序列来校验对象
validator.validate(user, orderedChecks.class);

  • 重定义默认校验组
  1. Bean Validation Annotation
    在类上使用@GroupSequence注解,如下代码示例3中所示
  2. Hibernate Validator Annotation
    在类上使用@GroupSequenceProvider注解,并自定义provider实现DefaultGroupSequenceProvider接口,详见附录hibernate validator文档2.3.2.2

代码示例3

1
2
3
4
5
6
7
//定义校验序列
@GroupSequence({"sexGroup", "usernameGroup"})
public class User {
...
}
//使用指定校验序列来校验对象,其实就是使用sexGroup和usernameGroup
validator.validate(user, Default.class);

4. 内置约束条件

  1. Bean Validate规定的约束条件
    详见附录hibernate validator文档2.4.1
  2. Hibernate独有的约束条件
    详见附录hibernate validator文档2.4.2

5. 多种方式创建validator实例

1
2
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
1
2
3
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) .configure()
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
1
2
3
4
5
ValidatorFactory validatorFactory = Validation.byDefaultProvider()
.configure()
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();

6. 自定义约束规则(不常用)

步骤如下,详见附录hibernate validator文档3.1

  • 创建约束标注
  • 实现一个验证器
  • 定义默认的验证错误信息

7. XML配置文件(不提倡)

详见附录hibernate validator文档4.1

8. 通过Configuration自定义验证失败提示信息等特性(不常用)

由于默认Validator基本满足日常使用,因此对此内容简介,详见附录hibernate validator文档5.1

1
2
3
4
5
6
7
8
Configuration<?> config = Validation.byDefaultProvider().configure();
config.messageInterpolator(new MyMessageInterpolator())
.traversableResolver( new MyTraversableResolver())
.constraintValidatorFactory(new MyConstraintValidatorFactory());
ValidatorFactory factory = config.buildValidatorFactory();
Validator validator = factory.getValidator();

配置简单说明:

  • messageInterpolator
    验证失败提示信息解析
  • traversableResolver
    某些情况下, 我们可能不应该去获取一个属性的状态。比如延迟加载的属性或者与JPA中涉及到关联关系的时候。当验证这两种情况的属性的时候,很可能会触发一次对数据库的查询.可以通过TraversableResolver接口来控制能否访问某一个属性。
    Hibernate Validator默认提供两种实现,其中DefaultTraversableResolver
    对isReachable()和isTraversable()俩方法永远返回true。
    可以自定义实现TraversableResolver接口,重写两种方法,然后通过configuration配置validator
  • constraintValidatorFactory
    自定义约束校验器的工厂类

9. hibernate validator 独有的特性(区别于Bean Validation)

Hibernate Validator相对于Bean Validation有很多独有的特性,这里只说一下其中被Bean Validation列入包含计划的特性

  • 方法约束:

    使用相同的方式(如注解方式),可以对方法参数、返回值等信息进行校验。
    为了可移植性,平时都尽可能只使用Bean Validation提供的特性,所以此特性只需了解即可,待Bean Validation纳入标准后再进行使用

附录

Hibernate Validator4.2.0 在线文档