javax.validation分組校驗(詳解版)


為什么使用分組?
場景:
比如:
package com.springbootvalidation.entity;

import com.springbootvalidation.common.BaseModel;
import com.springbootvalidation.common.Group;
import lombok.Data;

import javax.validation.constraints.NotBlank;

/**
 * Created by Administrator on 2021/9/20.
 */
@Data
public class UserEntity extends BaseModel{
    private static final long serialVersionUID = -6967633787950779639L;

    /*
        沒有加分組groups的屬於Default默認分組。
        添加分組groups的屬於自定義分組,不屬於默認分組。
        validation校驗默認使用默認分組校驗,不會校驗自定義分組。
        因此如果需要校驗自定義分組的字段,需要校驗的時候加上自定義分組,如:
        ValidationUtil.validate(userEntity, Default.class, Group.AddGroup.class);
        或者在controller方法體加上@RequestBody @Validated({Default.class, Group.AddGroup.class}) UserEntity userEntity, BindingResult bindingResult
     */
    /**
     * 用戶名
     */
    @NotBlank(message = "用戶名不能為空!",groups = Group.AddGroup.class)
    private String userName;

    /**
     * 密碼
     */
    @NotBlank(message = "密碼不能為空!")
    private String password;
}
在用戶注冊的時候或者登錄的時候,我需要用戶名和密碼都進行非空校驗。但是我在對用戶進行分組的時候,我有一個分組實體,里面有分組名稱和List<UserEntity>。
那么我在保存的時候,我會保存一個group和user的關系表,里面存分組id和userName。
這時候我在保存關系的時候,我只需要對用戶名進行非空校驗,而不需要對密碼進行非空校驗。這時候該怎么辦呢?再定義一個user實體password不加注解校驗?或者單獨程序校驗而不用注解校驗?
其實javax.validation已經為我們考慮好了,不用那么麻煩。只需要對要校驗的字段進行分組即可。

javax.validation分組校驗:
注意:
沒有加分組
groups的屬於Default默認分組。
添加分組
groups的屬於自定義分組,不屬於默認分組。
validation校驗默認使用默認分組校驗,不會校驗自定義分組。
因此如果需要校驗自定義分組的字段,需要校驗的時候加上分組,如
:
ValidationUtil.validate(userEntity, Default.class, Group.AddGroup.class);
或者在controller方法入參加上@RequestBody @Validated({Default.class, Group.AddGroup.class}) UserEntity userEntity, BindingResult bindingResult
不想要校驗某個分組的字段,把某個分組給去掉就好了。
1.pom依賴:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.springbootvalidation</groupId>
    <artifactId>springboot-validation</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <pagehelper.version>5.1.2</pagehelper.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
    </parent>

    <dependencies>
        <!-- 集成web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 集成lombok 框架 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- Mybatis分頁插件包 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>${pagehelper.version}</version>
        </dependency>
    </dependencies>
</project>
 
         
         
        

2.項目結構:

 BaseModel:

package com.springbootvalidation.common;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;

import java.io.Serializable;
import java.util.Date;

/**
 * Created by Administrator on 2021/9/20.
 */
@Data
public class BaseModel implements Serializable{
    private static final long serialVersionUID = 4559817637980468562L;

    /**
     * 創建人
     */
    private String createUser;

    /**
     * 創建時間
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime;

    /**
     * 修改人
     */
    private String updateUser;

    /**
     * 修改時間
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date updateTime;
}

BaseResponse:

package com.springbootvalidation.common;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.github.pagehelper.PageInfo;
import lombok.Getter;

import java.util.HashMap;
import java.util.Map;

/**
 * 返回實體
 * @className: BaseResponse
 * @author: admin
 * @date: 2021/9/20 12:36
 */
@Getter
@JsonInclude(JsonInclude.Include.NON_NULL)
public class BaseResponse {
    /**
     * 成功返回值
     */
    private final static String SUCCESS_CODE = CodeConts.SUCCESS;

    /**
     * 失敗默認返回值
     */
    private final static String FAILED_CODE = CodeConts.FAILURE;


    /**
     * 成功回復報文包含的對象
     */
    @JsonProperty("data")
    private final Object data;
    /**
     * 返回值
     */
    @JsonProperty("status")
    private final String status;
    /**
     * 返回msg
     */
    @JsonProperty("message")
    private final String message;
    /**
     * 返回msg
     */
    @JsonProperty("pages")
    private final Integer pages;
    /**
     * 返回msg
     */
    @JsonProperty("pageNum")
    private final Integer pageNum;
    /**
     * 返回msg
     */
    @JsonProperty("pageSize")
    private final Integer pageSize;
    /**
     * 其余參數
     */
    @JsonProperty("params")
    private final Map<String, Object> params;

    private BaseResponse(final Object data, final String status, final String message,
                         final Integer pages, final Integer pageNum, final Integer pageSize,
                         final Map<String, Object> params) {
        this.data = data;
        this.status = status;
        this.message = message;
        this.params = params;
        this.pages = pages;
        this.pageNum = pageNum;
        this.pageSize = pageSize;
    }

    /**
     * 構造成功報文builder
     *
     * @return 成功報文builder builder
     */
    public static Builder successCustom() {
        return successCustom("操作成功!");
    }

    /**
     * 構造成功報文builder
     *
     * @param message the message
     * @return builder
     */
    public static Builder successCustom(final String message) {
        return new Builder(SUCCESS_CODE, message);
    }

    /**
     * 請求成功返回狀態碼和提示
     *
     * @param successCode 狀態碼
     * @param message     提示信息
     * @return builder
     */
    public static Builder successCustom(final String successCode, final String message) {
        return new Builder(successCode, message);
    }

    /**
     * 構造錯誤返回報文builder
     *
     * @param errorCode 錯誤碼
     * @param errorMsg  錯誤信息
     * @return 錯誤返回報文builder builder
     */
    public static Builder failedCustom(final String errorCode, final String errorMsg) {
        return new Builder(errorCode, errorMsg);
    }

    /**
     * Failed custom builder.
     *
     * @param errorMsg the error msg
     * @return the builder
     */
    public static Builder failedCustom(final String errorMsg) {
        return new Builder(FAILED_CODE, errorMsg);
    }

    /**
     * The type Builder.
     */
    public static final class Builder {
        /**
         * 返回值
         */
        private final String status;
        /**
         * msg
         */
        private final String message;
        /**
         * 返回msg
         */
        private Integer pages;
        /**
         * 返回msg
         */
        private Integer pageNum;
        /**
         * 返回msg
         */
        private Integer pageSize;
        /**
         * 其他參數
         */
        private final Map<String, Object> params = new HashMap<>();
        /**
         * 任意可json化的對象
         */
        private Object data;

        private Builder(final String status, final String message) {
            this.status = status;
            this.message = message;
        }

        /**
         * 添加參數信息
         *
         * @param key   the key
         * @param value the value
         * @return builder
         */
        public Builder addParam(final String key, final String value) {
            this.params.put(key, value);
            return this;
        }

        /**
         * 設置result data
         *
         * @param data the data
         * @return data
         */
        public Builder setData(final Object data) {
            this.data = data;
            return this;
        }

        /**
         * 設置result 分頁
         *
         * @param pageInfo the page info
         * @return data
         */
        public Builder setData(final PageInfo pageInfo) {
            this.data = pageInfo.getList();
            //當前頁
            this.pageNum = pageInfo.getPageNum();
            this.pageSize = pageInfo.getPageSize();
            this.pages = pageInfo.getPages();
            return this;
        }

        /**
         * build BaseResponse
         *
         * @return base response
         */
        public BaseResponse build() {
            return new BaseResponse(this.data == null ? "" : this.data, this.status, this.message,
                    this.pages, this.pageNum, this.pageSize, this.params);
        }
    }
}

CodeConts:

package com.springbootvalidation.common;

/**
 * Created by Administrator on 2021/9/20.
 */
public class CodeConts {
    public static String SUCCESS = "1000";
    public static String FAILURE = "1001";
    public static String SYSTEM_EXCEPTION = "4000";
}

Group:

package com.springbootvalidation.common;

/**
 * 驗證分組
 * @className: Group
 * @author: liuyachao
 * @date: 2021/9/20 12:56
 */
public class Group {
    public interface AddGroup{}
}

UserController:

package com.springbootvalidation.controller;

import com.springbootvalidation.common.BaseResponse;
import com.springbootvalidation.common.CodeConts;
import com.springbootvalidation.entity.UserEntity;
import com.springbootvalidation.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.groups.Default;

/**
 * Created by Administrator on 2021/9/20.
 */
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @PostMapping("/addUser")
    public BaseResponse addUser(@RequestBody @Validated({Default.class}) UserEntity userEntity, BindingResult bindingResult){
        try {
            if(bindingResult.hasErrors()){
                return BaseResponse.failedCustom(bindingResult.getFieldError().getDefaultMessage()).build();
            }
            return userService.addUser(userEntity);
        }catch (Exception e){
            e.printStackTrace();
            return BaseResponse.failedCustom(CodeConts.SYSTEM_EXCEPTION,"系統異常").build();
        }
    }

    @PostMapping("/updUser")
    public BaseResponse updUser(@RequestBody UserEntity userEntity){
        try {
            return userService.updUser(userEntity);
        }catch (Exception e){
            e.printStackTrace();
            return BaseResponse.failedCustom(CodeConts.SYSTEM_EXCEPTION,"系統異常").build();
        }
    }
}

UserEntity:

package com.springbootvalidation.entity;

import com.springbootvalidation.common.BaseModel;
import com.springbootvalidation.common.Group;
import lombok.Data;

import javax.validation.constraints.NotBlank;

/**
 * Created by Administrator on 2021/9/20.
 */
@Data
public class UserEntity extends BaseModel{
    private static final long serialVersionUID = -6967633787950779639L;

    /*
        沒有加分組groups的屬於Default默認分組。
        添加分組groups的屬於分組,不屬於默認分組。
        validation校驗使用默認分組校驗,不會校驗自定義分組。
        因此如果需要校驗自定義分組的字段,需要校驗的時候加上分組,如:
        ValidationUtil.validate(userEntity, Default.class, Group.AddGroup.class);
        或者在controller方法體加上@RequestBody @Validated({Default.class, Group.AddGroup.class}) UserEntity userEntity, BindingResult bindingResult
     */
    /**
     * 用戶名
     */
    @NotBlank(message = "用戶名不能為空!",groups = Group.AddGroup.class)
    private String userName;

    /**
     * 密碼
     */
    @NotBlank(message = "密碼不能為空!")
    private String password;
}

UserService:

package com.springbootvalidation.service;

import com.springbootvalidation.common.BaseResponse;
import com.springbootvalidation.entity.UserEntity;

/**
 * Created by Administrator on 2021/9/20.
 */
public interface UserService {
    BaseResponse addUser(UserEntity userEntity);

    BaseResponse updUser(UserEntity userEntity);
}

UserServiceImpl:

package com.springbootvalidation.service.impl;

import com.springbootvalidation.common.BaseResponse;
import com.springbootvalidation.common.CodeConts;
import com.springbootvalidation.common.Group;
import com.springbootvalidation.entity.UserEntity;
import com.springbootvalidation.service.UserService;
import com.springbootvalidation.utils.ValidationUtil;
import org.springframework.stereotype.Service;

import javax.validation.groups.Default;
import java.util.List;

/**
 * Created by Administrator on 2021/9/20.
 */
@Service
public class UserServiceImpl implements UserService{
    @Override
    public BaseResponse addUser(UserEntity userEntity) {
//        BaseResponse validateResult = validateParam(userEntity);
//        if(!validateResult.getStatus().equals(CodeConts.SUCCESS)){
//            return validateResult;
//        }
        System.out.println("插入成功");
        return BaseResponse.successCustom().build();
    }

    private BaseResponse validateParam(UserEntity userEntity) {
        List<String> validateError = ValidationUtil.validate(userEntity, Default.class, Group.AddGroup.class);
        if (validateError != null && validateError.size() > 0) { // validateError.get(0)
            return BaseResponse.failedCustom(validateError.get(0)).build();
        }
        return BaseResponse.successCustom().build();
    }

    @Override
    public BaseResponse updUser(UserEntity userEntity) {
        System.out.println("更新成功");
        return BaseResponse.successCustom().build();
    }
}

ValidationUtil:

package com.springbootvalidation.utils;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.xml.bind.ValidationException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
* @description: 校驗工具類
* @author liuyachao
* @date 2021/9/18 16:38
*/
public class ValidationUtil {
    private static Validator validator;

    static {
       ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
       validator = vf.getValidator();
    }
    /**
      * javax.validation注解校驗
      * @throws ValidationException
      * @throws ValidationException
      * @Description: 校驗方法
      * @param t 將要校驗的對象
      * @throws ValidationException
      * void
      * @throws
     */
    public static <T> List<String> validate(T t){
        return validate(t,null);
    }

    public static <T> List<String> validate(T t, Class<?>... groups){
        Set<ConstraintViolation<T>> set;
        if(groups != null && groups.length > 0){
            set = validator.validate(t,groups);
        }else{
            set = validator.validate(t);
        }
        List<String> validateError = new ArrayList<>();
        if(set.size()>0){
            for(ConstraintViolation<T> val : set){
                validateError.add(val.getMessage());
            }
        }
        return validateError;
    }
}

App:

package com.springbootvalidation;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * Created by Administrator on 2021/9/20.
 */
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class,args);
    }
}

我們看下在controller的方法中校驗:

@PostMapping("/addUser")
    public BaseResponse addUser(@RequestBody @Validated({Default.class}) UserEntity userEntity, BindingResult bindingResult){
        try {
            if(bindingResult.hasErrors()){
                return BaseResponse.failedCustom(bindingResult.getFieldError().getDefaultMessage()).build();
            }
            return userService.addUser(userEntity);
        }catch (Exception e){
            e.printStackTrace();
            return BaseResponse.failedCustom(CodeConts.SYSTEM_EXCEPTION,"系統異常").build();
        }
    }

 javax.validation默認是Default默認分組。上面@Validated({Default.class})表示默認分組,也可以只用@Validated或者@Valid表示,去掉({Default.class}),這樣效果等同於@Validated({Default.class})表示只是用默認分組校驗。

默認分組校驗不會校驗AddGroup分組的字段。即不會校驗userName字段不能為空。

這樣的話如果其他地方用到此實體,而不想要校驗userName,那么只用默認分組校驗就可以了。因為默認分組校驗只會校驗默認分組的字段(即不加任何分組的字段),不會校驗自定義分組的字段(如:加了groups = Group.AddGroup.class)。

如果注冊或者添加的時候,既要校驗userName,又要校驗password。那么只需要再controller的分組上加上自定義分組的class即可。如下:

@RequestBody @Validated({Default.class, Group.AddGroup.class}) UserEntity userEntity, BindingResult bindingResult

總結:

那些不管在哪兒使用都要校驗的字段,我們用默認分組(不加任何分組)即可。如果有些字段在這里需要校驗,而在其他地方不需要校驗,那么把這些字段加上分組即可,

如果要校驗自定義分組的字段,只需要在校驗的時候加上Group.AddGroup.class即可。

只校驗默認分組的字段:只需要Default.class。

只校驗自定義分組的字段:只需要Group.AddGroup.class。

需要校驗默認分組又需要校驗自定義分組的字段:加上Default.class,Group.AddGroup.class。

分組的作用就是區分有些字段我需要校驗,而有些字段我不需要校驗,那么我校驗的時候選擇要校驗的分組即可,不校驗的字段的分組不選擇。

校驗字段的方式有兩種:

方式1:在controller的方法入參中加上@Validated({Default.class, Group.AddGroup.class})和BindingResult bindingResult。

方式2:用ValidationUtil工具類校驗,ValidationUtil工具類的校驗使得校驗不局限在controller的方法入參中。想在任何時候,任何地方校驗,就在哪里添加工具類的校驗。

方式1:這是controller方法中加的分組校驗方式:

@PostMapping("/addUser")
    public BaseResponse addUser(@RequestBody @Validated({Default.class, Group.AddGroup.class}) UserEntity userEntity, BindingResult bindingResult){
        try {
            if(bindingResult.hasErrors()){
                return BaseResponse.failedCustom(bindingResult.getFieldError().getDefaultMessage()).build();
            }
            return userService.addUser(userEntity);
        }catch (Exception e){
            e.printStackTrace();
            return BaseResponse.failedCustom(CodeConts.SYSTEM_EXCEPTION,"系統異常").build();
        }
    }
 
        

@Validated({Default.class, Group.AddGroup.class})中自行添加和刪除要校驗的分組。

方式2:使用ValidationUtil工具類校驗(ValidationUtil工具類的代碼已貼在上面),下面是工具類的使用:

BaseResponse validateResult = validateParam(userEntity);
        if(!validateResult.getStatus().equals(CodeConts.SUCCESS)){
            return validateResult;
        }
private BaseResponse validateParam(UserEntity userEntity) {
        List<String> validateError = ValidationUtil.validate(userEntity, Default.class, Group.AddGroup.class);
        if (validateError != null && validateError.size() > 0) { // validateError.get(0)
            return BaseResponse.failedCustom(validateError.get(0)).build();
        }
        return BaseResponse.successCustom().build();
    }

ValidationUtil.validate(userEntity, Default.class, Group.AddGroup.class)自行添加或刪除需要校驗的分組。

校驗分組方便區分了同一個實體中哪些字段我們想要校驗,哪些字段在另外一些地方又不想要校驗。

我們只需要將實體中的字段分組,然后校驗的時候選擇分組即可。

3.測試結果:

1.只想要校驗Default默認分組(不加任何分組的字段,@NotBlank(message = "密碼不能為空!")):

List<String> validateError = ValidationUtil.validate(userEntity, Default.class);//, Group.AddGroup.class

 userName是自定義分組的字段:

@NotBlank(message = "用戶名不能為空!",groups = Group.AddGroup.class)

這個字段不在默認分組中,不會校驗。

password在默認分組中,會校驗:

 

 2.只想要校驗AddGroup的字段(@NotBlank(message = "用戶名不能為空!",groups = Group.AddGroup.class),帶groups的):

 List<String> validateError = ValidationUtil.validate(userEntity, Group.AddGroup.class);

 userName是自定義分組的字段,為空,進行了非空校驗。

password是默認分組的字段,不是自定義分組的字段,不會校驗:

 

 3.既想要校驗默認分組字段又想要校驗自定義分組字段,那么把要校驗的字段的分組都加上:

List<String> validateError = ValidationUtil.validate(userEntity, Default.class, Group.AddGroup.class);

userName為空:

 password為空:

 兩個字段都進行了校驗。

 不同的分組,校驗的字段不同。這樣實現了對是否需要校驗的字段的區分。即有些地方我需要校驗某些字段,有些地方我不需要校驗那些字段。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM