Springboot hibernate validate校验

本项目使用环境
开发工具:Intellij IDEA 2017.1.3
springboot: 2.2.6
jdk:1.8.0_74
maven:3.3.9

备注:
我们平时写代码时对于某个实体需要做判空校验或则一些其他校验,平时我们可能会在代码中一行行判断;此时我们经常会if(user.getUserName==null)…若判断字段较多的话可能就会比较麻烦,并且造成了代码的不简洁。我们可以借助于hibernate validate解决。我也是第一次用,有什么建议感谢留言(废话不多说,上“才艺”)

1、首先pom中引入所需要的包

<dependency>
     <groupId>org.hibernate</groupId>
     <artifactId>hibernate-validator</artifactId>
 </dependency>

2、创建user实体类

package com.example.demo.dto;
import com.example.demo.service.AddGroup;
import com.example.demo.service.ListGtOne;
import com.example.demo.service.SaveGroup;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.*;
import java.io.Serializable;
import java.util.List;
@Data
@GroupSequence({AddGroup.class,SaveGroup.class,User.class})
public class User implements Serializable {
    /**
     * id
     */
    @NotNull(message = "用户id不能为空", groups = SaveGroup.class)
    private Integer id;
    /**
     * 姓名
     */
    @NotBlank(message = "用户名称不能为空", groups = {AddGroup.class, SaveGroup.class})
    private String name;
    /**
     * 密码
     */
    @NotBlank(message = "密码不能为空")
    @Size(min = 6, max = 18)
    private String password;
    /**
     * 年龄
     */
    @Min(1)
    @Max(100)
    private Integer age;
    /**
     * 邮箱
     */
    @Email(regexp = "^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*\\.[a-zA-Z0-9]{2,6}$", message = "邮件格式错误")
    private String email;
    /**
     * 手机号
     */
    @NotBlank(message = "手机号不能为空")
    @Size(min = 11, max = 11)
    private String mobile;
    /**
     * 学习科目
     */
    @ListGtOne(message = "用户学习科目至少两科", groups = {SaveGroup.class})
    @Valid
    List<Subject> subjectList;
}

上面@NotBlank、@Size等注解就是表示当前字段满足要求,具体注释可以网上查找。

3、ValidateUtil为校验方法
这个可以自由发挥,网上一些比较优秀的博主定义了一个validateUtil类来处理,我这里直接写到了公共类里面,其他需要校验的类直接继承了一下!

package com.example.demo.controller;
import com.example.demo.dto.CommonResult;
import com.example.demo.dto.ResultUtil;
import com.example.demo.dto.User;
import com.example.demo.enumeration.ErrorCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.HibernateValidator;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import java.util.Iterator;
import java.util.Set;
@Slf4j
public abstract class BaseController {
    private static javax.validation.Validator validator;
    static {
        /**
         * 使用hibernate的注解来进行验证 failFast true仅仅返回第一条错误信息 false返回所有错误
         */
        validator = Validation
                .byProvider(HibernateValidator.class)
                .configure().failFast(true)
                .buildValidatorFactory()
                .getValidator();
    }

    /**
     * @param obj 校验的对象
     * @param <T>
     * @return
     */
    public static <T> Set<ConstraintViolation<T>> validate(T obj, Class... groupClasses) 		{
        Set<ConstraintViolation<T>> constraintViolations;
        if (groupClasses != null && groupClasses.length > 0) {
            constraintViolations = validator.validate(obj, groupClasses);
        } else {
            constraintViolations = validator.validate(obj);
        }
        return constraintViolations;
    }
    /**
     * 校验参数是否合法
     *
     * @param result
     * @return
     */
    public static CommonResult checkoutParams(Set<ConstraintViolation<User>> result) {
        if (result.size() > 0) {
            // 抛出检验异常
            Iterator<ConstraintViolation<User>> it = result.iterator();
            // 错误消息
            String message = "";
            while (it.hasNext()) {
                ConstraintViolation<User> str = it.next();
                message = str.getMessage();
            }
            return ResultUtil.returnError("-1", message);
        }
        //result小于等于0,表示验证通过
        return ResultUtil.returnSuccess(null);
    }
}

注意:使用hibernate的注解来进行验证 failFast true仅仅返回第一条错误信息 false返回所有错误(感兴趣的话可以把这里设置成false试试,看看会怎样)

这里需要说明一下(由于日常的业务比较复杂,这里运用了一个自定义注解).

详细说明
如下截图subjectCode,subjectName都不能为空,那上面是否生效呢?经过测试是发现是不生效的,需要加另外个注释@Valid才能生效;科目列表里需要需至少两个,即subjectList.size()>2,这时会发现当前注释无法满足需求,因此我们要自定义一个自己的注释,使之和@NotBlank等起到一样的效果。

subject实体

package com.example.demo.dto;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
@Data
public class Subject implements Serializable {
    @NotBlank(message = "科目编码不能为空")
    private String subjectCode;
    @NotNull(message = "科目名称不能为空")
    private String subjectName;
}

自定义注解说明

package com.example.demo.service;
import com.example.demo.service.impl.ListNotEmptyValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
/**
 * 自定义注解,验证学科不能少于2科
 */
@Constraint(
        validatedBy = {ListNotEmptyValidator.class}
)
public @interface ListGtOne {
    /**
     * 以下方法仿照@NotBlank注释
     */
    String message() default "列表数据为空!";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

接下来就是ListGtOne对应的实现,只需要继承ConstraintValidator既可:(既:实现这个接口)

package com.example.demo.service.impl;
import com.example.demo.service.ListGtOne;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Collection;

public class ListNotEmptyValidator implements ConstraintValidator<ListGtOne, Collection> {
    public void initialize(ListGtOne listNotEmpty) {
    }
    public boolean isValid(Collection collection, ConstraintValidatorContext constraintValidatorContext) {
        if (collection == null || collection.size() < 2) {
            return false;
        }
        return true;
    }
}

这个时候,自定义注解就已经处理好了;接下来就是怎么运用了。如下:(@ListGtOne)

    /**
     * 学习科目
     * @ListGtOne为自定义注解的引用 @ + 自定义的注解(ListGtOne)
     */
    @ListGtOne(message = "用户学习科目至少两科")
    @Valid
    List<Subject> subjectList;

分组校验
由于需求的不同,所以校验的字段也会不同;我们不可能因为某个字段的校验规则不同;去把代母重复化,这样太麻烦了,和我们想要的简洁就有一定的差距了。所以这里我们用到了分组校验!
实现如下:
这里我还是拿user实体类做测试,如上(user实体类中 id,name就用到了分组检验),这里模拟了一个常见的引用场景,新增,和更新(我们都知道,新增的时候我们是不用去校验主键id的;更新则不然,这个时候分组校验就友好的帮助我们解决了这个问题了!)

**关键字:groups **

 /**
     * id
     */
    @NotNull(message = "用户id不能为空", groups = SaveGroup.class)
    private Integer id;
    /**
     * 姓名
     */
    @NotBlank(message = "用户名称不能为空", groups = {AddGroup.class, SaveGroup.class})
    private String name;

说明一下:name里面 groups = {AddGroup.class, SaveGroup.class },这里指的是新增的时候,更新的时候都需要校验,但是已新增优先于更新

下面介绍一下AddGroup.class,SaveGroup.class;其实这两个就是一个接口并且不需要去实现它,名字可以顺便取;只要能做区分分组就行;当然不写时它会有个默认的分组 Default.class,所有不填写的都会划入这个分组。

如下图:(AddGroup.class, SaveGroup.class)这是两个接口

package com.example.demo.service;

/**
 * 用于接口Api验证分组
 */
public interface AddGroup {
}



package com.example.demo.service;
/**
 * 用于接口Api验证分组
 */
public interface SaveGroup {
}

说了这么多:下面看看怎么用!

package com.example.demo.controller;

import com.example.demo.dto.CommonResult;
import com.example.demo.dto.ResultUtil;
import com.example.demo.dto.Subject;
import com.example.demo.dto.User;
import com.example.demo.enumeration.ErrorCodeEnum;
import com.example.demo.service.AddGroup;
import com.example.demo.service.SaveGroup;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.ConstraintViolation;
import javax.validation.groups.Default;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Slf4j
@RestController
@RequestMapping("/hello")
public class HellorController extends BaseController {
    /**
     *
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "test", method = RequestMethod.GET)
    public Object test() {
        try {
            User user = new User();
            user.setId(null);
            user.setName("");
            user.setPassword("12312312312");
            user.setMobile("15120322112");
            user.setEmail("871331263@qq.com");
            List<Subject> subjectList = new ArrayList<Subject>();
            Subject subject = new Subject();
            subject.setSubjectCode("A001");
            subject.setSubjectName("");
            subjectList.add(subject);
            user.setSubjectList(subjectList);
            //调用ValidateUtil中的方法校验
            Set<ConstraintViolation<User>> result = validate(user,Default.class, AddGroup.class,SaveGroup.class);
            //校验参数是否合法
            CommonResult resultUtil = checkoutParams(result);
            log.info("test params:{}", user);
            if (!resultUtil.isSuccess()) {
                return ResultUtil.returnError(ErrorCodeEnum.ERROR.getCode(), resultUtil.getMessage());
            }
            return ResultUtil.returnSuccess(user);
        } catch (Exception e) {
            log.error("test error:{}", e.getMessage());
            return ResultUtil.returnError(ErrorCodeEnum.ERROR);
        }
    }
}

这里要注意一下:validate(user,Default.class, AddGroup.class,SaveGroup.class);user就是我们定义好的实体类,后面的Default、AddGroup、SaveGroup 如果不需要用到分组校验,则不需要写,默认会根据Default来校验!另外这三个分组的顺序是可以调整的,校验时优先顺序会根据自己调整的顺序来进行校验!

以下是返回的实体类和枚举定义:测试的时候用的,可自由发挥!

CommonResult实体类如下:

package com.example.demo.dto;
import lombok.Data;
import java.io.Serializable;

@Data
public class CommonResult<T> implements Serializable {
    /**
     * serialVersionUID:.
     */
    private static final long serialVersionUID = -7268040542410707954L;
    /**
     * 是否成功
     */
    private boolean success = false;
    /**
     * 返回信息
     */
    private String message;
    /**
     * 装在数据
     */
    private T data;
    /**
     * 错误代码
     */
    private String code;
    /**
     * 默认构造器
     */
    public CommonResult() {

    }
    /**
     * @param success 是否成功
     * @param message 返回的消息
     */
    public CommonResult(boolean success, String message) {
        this.success = success;
        this.message = message;
    }
    /**
     * @param success 是否成功
     */
    public CommonResult(boolean success) {
        this.success = success;
    }
    /**
     * @param code    error code
     * @param message success or error messages
     */
    public CommonResult(String code, String message) {
        this.code = code;
        this.message = message;
    }
    /**
     * @param success 是否成功
     * @param message 消息
     * @param data    数据
     */
    public CommonResult(boolean success, String message, T data) {
        this.success = success;
        this.message = message;
        this.data = data;
    }
}

ResultUtil实体类如下:

package com.example.demo.dto;
import com.example.demo.enumeration.ErrorCodeEnum;
public class ResultUtil {
    /**
     * return success
     * @param data
     * @return
     */
    public static <T> CommonResult<T> returnSuccess(T data) {
        CommonResult<T> result = new CommonResult();
        result.setCode(ErrorCodeEnum.SUCCESS.getCode());
        result.setSuccess(true);
        result.setData(data);
        result.setMessage(ErrorCodeEnum.SUCCESS.getDesc());
        return result;
    }
    /**
     * return error
     *
     * @param code error code
     * @param msg  error message
     * @return
     */
    public static CommonResult returnError(String code, String msg) {
        CommonResult result = new CommonResult();
        result.setCode(code);
        result.setData("");
        result.setMessage(msg);
        return result;
    }
    /**
     * use enum
     *
     * @param status
     * @return
     */
    public static CommonResult returnError(ErrorCodeEnum status) {
        return returnError(status.getCode(), status.getDesc());
    }
}

访问一下就可以看到验证的消息了。
我这里的验证地址是:http://localhost:8888/hello/test
提示的错误消息:{“success”:false,“message”:“用户名称不能为空”,“data”:"",“code”:"-1"}

最后在说明一下:因为我这里在调用的时候,Default.class分组我放到了第一位,所以Default优先于去他的分组,感兴趣的可以重置一下 test方法中user实体类的参数值
,然后在把 且user实体类中和test方法中

@GroupSequence({AddGroup.class,SaveGroup.class,User.class})
 //调用ValidateUtil中的方法校验
  Set<ConstraintViolation<User>> result = validate(user,  Default.class,SaveGroup.class, AddGroup.class);

分组的顺序调整一下,就可以看到不同的效果了!

最后

大神勿喷,刚入门的菜鸟!感激不尽。。_ _

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值