Spring boot 结合hibernate-validate校验数据

Spring boot 结合hibernate-validate校验数据学习

spring boot的web起步依赖中已经引入的hibernate-validate数据校验依赖,只要项目依赖的web起步依赖就无需再引入依赖。

依赖引入

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.1.4.Final</version>
</dependency>

常用注解介绍

validate通过在字段上加入注解来实现一个个指定的校验功能,常用的注解有:

注解 描述
@NotNull 不能为空
@NotBlank 字符串不能为空也不能为空串
@Null 被校验对象只能为null
@Size(min=2,max=6) 被校验字符串长度大小只能在2到6之间
@Max(value=7) 被校验参数最大只能为7
@AssertTrue 标注的属性值必须为true
@AssertFalse 标注的属性值必须为false
@Past 时间必须是过去的时间
@Future 将来的时间
@Pattern 用于指定一个正则表达式
@NotEmpty 字符串内容非空
@Email 邮箱
@Range 用于指定数字,注意是数字的范围,有两个值,min以及max
@Digits 内容必须是数字
@PositiveOrZero 0或者整数

简单使用

使用validate进行数据的校验可以将校验注解定义在对象字段或者是方法参数中

在pojo中定义校验注解
public class Entity {

    @NotBlank(message = "用户名不能为空")
    private String username;

    @NotBlank(message = "密码不能为空")
    @Length(min = 6,message="长度必须大于等于6")  //密码长度必须大于六
    private String password;

    @Email(message="非法邮件地址")
    private String email;

    @NotBlank(message = "手机号码不能为空")
    @Length(min = 11, max = 11, message = "手机号只能为11位")
    @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误")
    private String phone;

}

在方法参数上定义校验注解

注意,在方法的参数上使用校验注解需要在类名上添加@Validated注解

@RestController
@RequestMapping("/demo")
@Validated //参数校验
public class DemoController {

    @PostMapping("/search/{page}/{size}")
    public Entity demo(@RequestBody @Validated Entity entity,
                       @PathVariable("page") @Min(value = 0)  Integer page,
                       @PathVariable("size") @Min(value = 0) Integer size){
        System.out.println(entity.getUsername()+"---"+entity.getPassword());
        System.out.println(page+"----------"+size);
        return entity;

    }
}

统一异常处理

若定义对象字段上,则若是校验出数据无效,则会抛出:

MethodArgumentNotValidException extends Exception  //方法参数无效异常

若是定义在方法参数中,若数据无效,则抛出:

ConstraintViolationException extends ValidationException  //约束异常

我们可以在spring boot中定义一个全局异常处理器,用于拦截校验出现异常后返回的提示信息

/**
 * 全局异常处理器
 */
@ControllerAdvice
public class ExceptionGlobalHandler {

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result handle(Exception e){
        //System.out.println(e);
        String message = null;
        if(e instanceof MethodArgumentNotValidException){  //参数校验-对象数据校验异常
            //获取提示信息
            for (ObjectError error : ((MethodArgumentNotValidException) e).getBindingResult().getAllErrors()) {
                message = error.getDefaultMessage();//获取自定义的错误提示信息
            }
        }else if(e instanceof ConstraintViolationException){  //方法参数校验异常
            Iterator<ConstraintViolation<?>> iterator = ((ConstraintViolationException) e).getConstraintViolations().iterator();
            ConstraintViolation<?> next = iterator.next();
            message = next.getMessage();  //获取自定义的错误提示信息
        }else{
            message = e.getMessage(); //其他异常
        }
        return Result.error().setMessage(message);
    }
}

扩展一:分组校验

在使用hibernate-validate进行数据校验时,我们有时候会进行分情况校验数据,例如:在新增操作时,我们不进行id不为空校验,但是在修改操作时,我们需要进行id不为空的校验。而这,若当当使用@NotNull进行校验,则在insert操作时也会进行为空校验,这不是我们想要的。此时,我们可以对校验进行分组,在不同的情况下使用不同的分组进行数据的校验。

每一组分组都需要一个接口来作为分组的标志,但接口内可以不添加内容

一般的分组有:新增insert;修改ipdate;删除delete;查询select

我是用内部接口来管理这些标记类接口

public class ValidateGroupFalg {
    /**
     * 新增操作组
     */
    public static interface  InsertFlag{}
    /**
     * 删除操作组
     */
    public static interface  DeleteFlag{}
    /**
     * 修改操作组
     */
    public static interface  UpdateFlag{}
    /**
     * 查询操作组
     */
    public static interface  SelectFlag{}
}

对于校验用户id,进行分组校验

@TableName("tb_demo")
public class TbDemo implements Serializable {

    @TableId(type = IdType.INPUT)
    @NotNull(message = "请指定要修改的编号",groups = ValidateGroupFalg.UpdateFlag.class)
    private String id;
}

在使用时只需要在接收方法的参数中,添加校验注解,并指定分组标记即可

public  Result updateById(@RequestBody @Validated(ValidateGroupFalg.UpdateFlag.class) TbDemo tbDemo){
   // ....
}

扩展一:自定义校验

当我们需要进行某些指定的校验时,我们可以通过hibernate-validator提供的AIP创建自定义校验。

步骤

1、创建注解,并指定数据处理类

2、创建数据处理类,实现内部方法

3、使用注解

实现

1、创建注解,并指定数据处理类

/**
 * 时间字符串格式校验注解
 */
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = DateTimeValidator.class)  //用于指定数据处理类
public @interface DateFormatValidate {
    String message() default "时间格式错误";

    String format() default "yyyy-MM-dd";

    Class<?>[] groups() default {};  

    Class<? extends Payload>[] payload() default {};
}

2、创建数据处理类,实现内部方法

import com.bishe.common.validate.DateFormatValidate;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.text.SimpleDateFormat;

/**
 * 时间字符串格式校验
 */
public class DateTimeValidator implements ConstraintValidator<DateFormatValidate, String> {

   private DateFormatValidate dateFormatValidate ;  //注解引用

    
   public void initialize(DateFormatValidate constraint) {
      this.dateFormatValidate = constraint;
      //System.out.println("初始化方法调用。。。");
   }

    //校验方法,成功返回true;失败返回false,validate会进行判断,false抛出异常
   public boolean isValid(String value, ConstraintValidatorContext context) {
      //System.out.println("开始校验。。。");
      if (value == null) {
         return true;
      }
      String format = dateFormatValidate.format();  //获取自定义的时间格式

      if (value.length() != format.length()) {  //若2020-10-10 与YYYY-mm-dd 长度不同,返回false
         return false;
      }

       try {
     	 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format); //创建时间格式对象
         simpleDateFormat.parse(value);  //尝试通过格式转化被校验参数,成功返回true,失败,返回false
      } catch (Exception e){
         return false;  //转化失败
      }
      return true; //校验成功
   }
}

3、使用注解

@DateFormatValidate(format = "yyyy/MM/dd", message = "格式错误,正确格式为:yyyy/MM/dd")
    private Date birthday;

完成!

上一篇:Java全栈工程师【已完结】


下一篇:Hibernate之二级缓存