Hibernate Validator的使用

前言:最近项目里面用到了hibernate-validator做接口的参数校验,第一感觉很好用,但是返回的提示还不够友好,这里全面了解一下这个项目的用法和功能。

此工具的中文文档我有传到我的下载中 可以在这里下载 https://download.csdn.net/download/zhanjunpeng01/11217481

什么是Hibernate validator呢?它是基于JSR-303接口的标准,在数据模型上添加需要校验的方式(基于注解或者XML扩展),实现对数据模型的验证框架。

为什么要用?1. 将数据验证从业务代码中抽离,降低了业务代码的复杂性。2. 可以在任意地方对实体进行校验,是否是符合标准的数据。 3. 可以减少很大的工作量,尤其是在数据模型比较庞大的地方。

一、构建基本工程


首先可以根据官方的maven archetype创建一个简单的项目,了解一下这个工具的用法
 

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

也可以直接在idea里面创建maven项目,输入上面的参数创建出项目:

Hibernate Validator的使用_第1张图片

创建出来的工程即包含了一些基础代码和测试类

如果创建不出来,可以在maven里面加一下阿里云的镜像试试

    
        nexus-aliyun
        *
        Nexus aliyun
        http://maven.aliyun.com/nexus/content/groups/public
     

 

二、简单介绍重要知识点

基础用法先参考代码中的Car类,以及CarTest类

给javabean添加校验注解有三种方式

1. 添加到类的字段上(field level)

2. 添加到字段的get方法上(property level)

3. 添加到类上(class level)

注解到字段上和属性上的区别好像不太大,官方也比较推荐注解到字段上,

类级别的验证,则是由于某些场景,需要多个字段同时满足条件的时候比较有用

 

在父类中定义的约束对子类一样有效,就像定义在子类上一样的效果

@Valid用于级联校验,如果一个类中引入了另外一个类,比如 ClassRoom类中有 List 的属性,那么在这个属性上添加@Valid,在这个属性不为空的情况下,会对关联属性的实体进行数据校验。而如果@Valid标注的属性是null,则不会进行校验

 

Validator接口是HV(Hibernate Validator)中对实体进行校验操作的最重要的一个接口。对校验的功能进行了定义

下面是获取Validator默认实现的代码

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();

这个接口提供了三个对实体校验的方法:大致的使用方法为

Set violations = validator.xxx( [xxx]... );

返回的violations结果中包含了所有不符合规则定义的错误集合,可以接收0个或多个校验组作为参数

三个校验方法分别为:

1. 对对象的校验: validate(obj)

2. 对字段的校验: validateProperty(obj, fieldName)

3. 校验某个字段使用某个值是否是合法的:validateValue(obj, fieldName, value)

返回的ConstraintViolation集合的主要方法如下:

Hibernate Validator的使用_第2张图片

三、关于验证失败的错误信息:

每个验证失败的消息提示都来自于一个消息模板,声明一个注解校验约束的时候,可以通过设置message属性来更改这个模板。模板中还可以使用占位符来设置不确定的内容,如下:

Hibernate Validator的使用_第3张图片

同时,你配置的MessageInterpolator可以用来解析这个模板,系统默认的MessageInterpolator会优先查找类路径下找名称
为ValidationMessages.properties的ResourceBundle, 如果能匹配不成功,则去HV自带的资源文件位于/org/hibernate/
validator/ValidationMessages.properties的ResourceBundle,如下图:

Hibernate Validator的使用_第4张图片

由此可见,HV支持国际化也是很方便的,正好可以在项目中用到,后面我将会在代码中测试一下这个功能。

 

4. 关于校验组:

校验组是校验注解中的一个属性groups,一个实体内的所有字段,可能在不同的情况下有不同的校验需求,下面先简单描述一下,详细的代码可以看官方文档第2.3章节进行了解。

比如,一个商品实体里面有商品名称,库存,是否已审核三个字段,其中商品是否能上架需要库存>0, 且是已审核的,而简单的保存商品信息则不需要检查。那么首先我们需要先定义一个接口表示一个组,这里我定义了一个空的接口OnSaleCheck.java ,使用接口作为组的定义而不是字符串是因为接口可以做到类型安全,而且对重构也比较友好

定义上架检查分组的接口类:

public interface OnSaleCheck {
}

在实体类中使用分组,注意看属性上面的注解属性groups = {OnSaleCheck.class}

public class Goods {

    @NotNull
    private String goodsName;

    @NotNull
    @Min(value = 1, groups = {OnSaleCheck.class})
    private Integer stockNum;

    @AssertTrue(groups = {OnSaleCheck.class})
    private boolean approvedPass;

    //get、set方法省略...
}

在测试类中使用:

    @Test
    public void goodsOnSaleCheck(){
        Goods goods = new Goods();
        goods.setGoodsName("测试");
        goods.setStockNum(0);
        goods.setApprovedPass(true);
        assertEquals(1, validator.validate(goods, OnSaleCheck.class).size());
        assertEquals(0, validator.validate(goods).size());

        goods.setGoodsName(null);
        assertEquals(2, validator.validate(goods, OnSaleCheck.class, Default.class).size());
    }

测试通过,最后两行的代码说明,使用了OnSaleCheck.class分组的校验,才会对该分组下面的属性做校验

需要注意的是,其实每个校验注解都会有一个分组,如果没有明确定义,则会使默认分组:javax.validation.groups.Default接口

而validate方法,可以接收多个校验组作为参数,如果你想校验所有的属性,那么在validate()方法再加上一个参数Default.class就可以啦

另外需要注意的是,如果是在validate方法中传入多个校验组,这些校验组的校验是没有顺序的,然而有些时候我们希望能够按照一定的顺序进行校验,那么需要再定义一个新的接口,并且用@GroupSequence注解这个接口。

但是,用@GroupSequence注解的接口,聚合了多个校验组,在发现第一个不符合条件的校验以后,就会返回,并不会再继续对后面的其它条件进行校验。

这里用校验组组序列(GroupSequence)做一个例子

定义一个新的接口 OnSaleCheckAll ,我们希望先校验基础信息,再校验是否可以上架

/**
 * Describe 先校验商品基础信息,再校验是否可以上架
 * Created by zhanjp on 2019/6/1
 */
@GroupSequence({Default.class, OnSaleCheck.class})
public interface OnSaleCheckAll {
}

测试:

Hibernate Validator的使用_第5张图片

这里我们可以发现,校验序列组的检查再发现第一个错误之后就返回了。

还有一个需要注意的地方,校验序列组不可包含循环引用,什么意思呢?就是校验序列组A中包含了B,而校验序列组B中又包含了A,这种情况是会报错的。

如果想在子类中重新去定义校验序列组,直接在子类上添加@GroupSequence注解就好了,不过不可再次在@GroupSequence中加入Default.class, 而是用"子类.class"替代,感觉这个不是常用,这里不再描述,感兴趣的可以看2.3.2节

@GroupSequenceProvider注解 是官方提供的一个非标准重新定义校验序列组的功能,相较于在子类上添加@GroupSequence改变父类的校验序列组,使用@GroupSequenceProvider可以按照一些条件动态添加校验组。

 

5. 关于支持的校验规则

校验规则分为三种,1. java默认支持的规则(注解位于javax.validation.constraints) 2. HV自己实现的规则 (位于org.hibernate.validator.constraints.impl),3. 客户端自定义(自己开发)

java支持的注解和HV支持的注解,直接可以在开发包中看,这里不再描述

主要介绍自定义校验规则

定义一个自定义的校验规则需要三个步骤:

1. 创建约束注解

2. 实现一个验证器

3. 定义默认的验证错误消息

按照文档上的例子来实现一下:创建一个校验字符串大小写的自定义校验规则

step1:

 

 

 

 

 

 

 

 

你可能感兴趣的:(工具)