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項目,輸入上面的參數創建出項目:

創建出來的工程即包含了一些基礎代碼和測試類

如果創建不出來,可以在maven裏面加一下阿里雲的鏡像試試

    <mirror>
        <id>nexus-aliyun</id>
        <mirrorOf>*</mirrorOf>
        <name>Nexus aliyun</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public</url>
    </mirror> 

 

二、簡單介紹重要知識點

基礎用法先參考代碼中的Car類,以及CarTest類

給javabean添加校驗註解有三種方式

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

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

3. 添加到類上(class level)

註解到字段上和屬性上的區別好像不太大,官方也比較推薦註解到字段上,

類級別的驗證,則是由於某些場景,需要多個字段同時滿足條件的時候比較有用

 

在父類中定義的約束對子類一樣有效,就像定義在子類上一樣的效果

@Valid用於級聯校驗,如果一個類中引入了另外一個類,比如 ClassRoom類中有 List<Student> 的屬性,那麼在這個屬性上添加@Valid,在這個屬性不爲空的情況下,會對關聯屬性的實體進行數據校驗。而如果@Valid標註的屬性是null,則不會進行校驗

 

Validator接口是HV(Hibernate Validator)中對實體進行校驗操作的最重要的一個接口。對校驗的功能進行了定義

下面是獲取Validator默認實現的代碼

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

這個接口提供了三個對實體校驗的方法:大致的使用方法爲

Set<ConstraintViolation> violations = validator.xxx( [xxx]... );

返回的violations結果中包含了所有不符合規則定義的錯誤集合,可以接收0個或多個校驗組作爲參數

三個校驗方法分別爲:

1. 對對象的校驗: validate(obj)

2. 對字段的校驗: validateProperty(obj, fieldName)

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

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

三、關於驗證失敗的錯誤信息:

每個驗證失敗的消息提示都來自於一個消息模板,聲明一個註解校驗約束的時候,可以通過設置message屬性來更改這個模板。模板中還可以使用佔位符來設置不確定的內容,如下:

同時,你配置的MessageInterpolator可以用來解析這個模板,系統默認的MessageInterpolator會優先查找類路徑下找名稱
爲ValidationMessages.properties的ResourceBundle, 如果能匹配不成功,則去HV自帶的資源文件位於/org/hibernate/
validator/ValidationMessages.properties的ResourceBundle,如下圖:

由此可見,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 {
}

測試:

這裏我們可以發現,校驗序列組的檢查再發現第一個錯誤之後就返回了。

還有一個需要注意的地方,校驗序列組不可包含循環引用,什麼意思呢?就是校驗序列組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支持的註解,直接可以在開發包中看,這裏不再描述

主要介紹自定義校驗規則

未完待續...

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章