• 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为空:

     两个字段都进行了校验。

     不同的分组,校验的字段不同。这样实现了对是否需要校验的字段的区分。即有些地方我需要校验某些字段,有些地方我不需要校验那些字段。

  • 相关阅读:
    MyBatis 知识点梳理
    SSH无密码登录的原理及配置
    Maven学习笔记
    阿里Java开发电话面试经历惨败
    Java生成验证码(二)
    Java生成验证码(一)
    Hibernate 知识点梳理
    数据结构线性表顺序表示 (二)
    replace tabs with the proper number of blanks
    数据结构线性表顺序表示 (三)
  • 原文地址:https://www.cnblogs.com/super-chao/p/15314566.html
Copyright © 2020-2023  润新知