Hibernate Validator 数据校验框架

文章目录

一、数据校验框架简介

1、JSR(Java 规范提案):Bean Validation

  • JSR:Java Specification Requests的缩写,意思是Java规范提案
    • 是指向JCP(Java Community Process) 提出新增一个标准化技术规范的正式请求
    • 任何人都可以提交JSR,以向Java平台增添新的API和服务
    • JSR已成为Java界的一个重要标准
  • Bean Validation就是这个JSR规范之一
    • Bean Validation是一个运行时的数据验证框架的标准

JSR303是专家组成员向JCP提交的第1版Bean Validation,即针对bean数据校验提出的一个规范,使用注解方式实现数据校验。后面有升级版本JSR349及JSR380。

JSR # Bean Validation官网地址:https://jcp.org/en/jsr/summary?id=bean+validation

  • JSR303 Bean Valiadation第一版 伴随着JAVAEE 6在2009年发布,Hibernate实现版本4.3.1.Final
  • JSR349 Bean Valiadation 1.1 伴随着JAVAEE 7在2013年发布,Hibernate实现版本5.1.1.Final
  • JSR380 Bean Valiadation 2.0 伴随着JAVAEE 8在2017年发布,完全兼容低版本的JAVA SE,Hibernate实现版本6.0.1.Final

Bean Valiadation 与 Hibernate Validation

  • JSR(Bean Valiadation)规定一些校验规范即校验注解,如@Null,@NotNull,@Pattern,位于javax.validation.constraints包下,只提供规范不提供实现
  • Hibernate Validation是对Bean Valiadation这个规范的实践,提供相应的实现,并增加一些其他校验注解,如@Length,@Range等等,位于org.hibernate.validator.constraints包下

2、javax.validation.api

  • Java在2009年的JAVAEE 6中发布了JSR303以及javax下的validation包内容
  • JSR Bean Validation 2.0 重要版本2.0.1.Final
xml 复制代码
<dependency>  
    <groupId>javax.validation</groupId>  
    <artifactId>validation-api</artifactId>  
    <version>2.0.1.Final</version>  
</dependency>
  • Bean Validation 2.0中包含的注解
  • Hibernate-Validator是Hibernate项目中的一个数据校验框架,是Bean Validation一种实现
  • Hibernate-Validator除了提供了JSR 303规范中所有内置constraint的实现,还有一些附加的constraint(约束)
  • JSR Bean Validation 2.0对应hibernate-validator的实现重要版本6.0.16.Final
xml 复制代码
<dependency>
  <groupId>org.hibernate.validator</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>6.0.16.Final</version>
</dependency>
  • Hibernate-Validator 扩展的注解
  • spring-boot-starter-web 2.1.4.RELEASE引入的就是是这个版本
  • hibernate-validator包下包含了validation-api,这个很好理解,做规范注解的实现肯定需要用到规范中定义的注解

3、jakarta.validation.api

  • Java8开始,Java EE改名为Jakarta EE,故javax.validation相关的api在jakarta.validation的包下
  • JSR Bean Validation 2.0 重要版本2.0.2
xml 复制代码
<dependency>  
    <groupId>jakarta.validation</groupId>  
    <artifactId>jakarta.validation-api</artifactId>  
    <version>2.0.2</version>  
</dependency>
  • springboot后面版本不再自动导入hibernate-validator,可以导入spring-boot-starter-validation:2.3.12.RELEASE
  • 虽然依赖名改为了jakarta,导包还是javax,应该是个过渡吧

二、SpringBoot基础使用

  • springboot项目导入依赖,低版本不需要导入,spring-boot-starter-web就包含了
xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

1、校验get请求参数

  • get请求校验参数类上需要添加@Validated注解
  • @Validated是springmvc提供注解,非javax.validation的注解
java 复制代码
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotEmpty;

@RestController
@Validated
public class UserController1 {
    @GetMapping("/getUser")
    public String getUser(@NotEmpty(message = "name不能为空") @RequestParam("name") String name,
                          @Max(value = 150) @RequestParam("age") Integer age) {
        return "success";
    }
}

执行请求:http://localhost:8080/getUser?name=\&age=200

  • 校验注解可以设置message属性(抛错提示),也可以不设置(默认中文错误提示)

2、校验post请求参数

  • post请求校验实体类上前面添加@Valid@Validated注解
    • 两者在这里作用相同,唯一区别是后者可以设置分组,后面会讲
  • @Valid是javax.validation提供注解
java 复制代码
import javax.validation.Valid;

@RestController
public class UserController2 {
    @PostMapping("/saveUser")
    public String saveUser(@Valid @RequestBody User user) {
        System.out.println(user);
        return "success";
    }
}
  • 请求实体添加校验注解
java 复制代码
import org.hibernate.validator.constraints.Range;
import org.hibernate.validator.constraints.Length;

import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

@Data
public class User {
    // 可以为 null,若不为 null,则长度为 [5, 17]
    @Length(min = 5, message = "userName长度须在[5,17]")
    private String userName;

    // @Size不能验证Integer,适用于String、List、Map、数组
    @Size(min = 1, max = 3, message = "password长度须在[1,3]")
    private String password;

    // list 可以为 null,若不为 null,则长度为 [1, 5]
    @Size(min = 1, max = 5, message = "list的Size在[1,5]")
    private List<String> list;

    @NotNull
    @Valid // 级联校验,该注解将会校验自定义类
    private DetailInfo detailInfo;
}

@Data
class DetailInfo {
    @NotEmpty(message = "name不能为空")
    private String name;

    // 匹配任何数字
    @Pattern(regexp = "^\\d+$", message = "itemCode需要数字")
    private String itemCode;

    @Range(min = 10, max = 100, message = "range在[10,100]之间")
    private int age;
}

3、常用注解

注解 说明 支持类型
@Null 必须为null
@NotNull 必须不为null
@NotEmpty 必须不为null且长度大于0 String、集合、Map、数组
@NotBlank 必须不为null且去除首尾空格后长度大于0 String
@AssertTrue 必须为true boolean和Boolean
@AssertFalse 必须为false boolean和Boolean
@Min(value) 必须是一个数字 其值必须大于或等于指定的最小值 值为null不校验 String、Number
@Max(value) 必须是一个数字 其值必须小于或等于指定的最大值 值为null不校验 String、Number
@DecimalMin(value) 必须是一个数字 其值必须大于或等于指定的最小值(支持小数) 值为null不校验 String、Number
@DecimalMax(value) 必须是一个数字 其值必须小于或等于指定的最大值(支持小数) 值为null不校验 String、Number
@Size(max, min) 元素的大小必须在指定的范围内 值为null不校验 String、集合、Map、数组
@Pattern(regexp = ) 正则表达式校验 值为null不校验
@Digits(integer,fraction) 验证整数位数和小数位数上限 值为null不校验 String、Number
@Past 必须是一个过去的日期 值为null不校验 日期类型
@Future 必须是一个将来的日期 值为null不校验 日期类型
@Email 必须是电子邮箱地址 值为null不校验
@Length(min=, max=) 字符串长度必须在指定的范围内 值为null不校验 String
@Range(min=, max=) 数字或字符串数值必须在指定范围内 值为null不校验 String、Number
@URI 必须是一个有效的URL字符串 值为null不校验 String

注意:值为null不校验的注解一般和@NotNull一起使用

4、分组校验

  • @Validated通过分组设置不同的校验注解
  • 默认所有注解分组为javax.validation.groups.Default,添加Default,为了没有分组的注解生效
java 复制代码
@RestController
public class UserController3 {

    @PostMapping("/saveUser")
    public String saveUser(@Validated(value = {User.SaveGroup.class, Default.class}) @RequestBody User user) {
        System.out.println(user);
        return "success";
    }

    @PostMapping("/updateUser")
    public String updateUser(@Validated(value = {User.UpdateGroup.class, Default.class}) @RequestBody User user) {
        System.out.println(user);
        return "success";
    }
}
java 复制代码
@Data
public class User {

    // 默认就是Default分组
    public interface SaveGroup {
    }
    public interface UpdateGroup {
    }

    // 新增时必须为空
    @Null(groups = SaveGroup.class)
    // 修改是必须不为空
    @NotNull(groups = UpdateGroup.class)
    private Long id;

    @NotEmpty
    private String userName;
}

5、自定义校验规则

  • 对于常用的注解的验证规则内容在xxxValidator中,如下
  • 自定义状态注解,validatedBy则是实现类的Class对象
java 复制代码
@Documented
@Constraint(validatedBy = {StatusValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
public @interface Status {

    String message() default "状态标识只能为0或1";

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

    Class<? extends Payload>[] payload() default {};
}
  • 注解实现类,ConstraintValidator的第一个泛型为自定义注解,第二个为支持类型
java 复制代码
public class StatusValidator implements ConstraintValidator<Status, String> {
	@Override
	public boolean isValid(String bool, ConstraintValidatorContext constraintValidatorContext) {
		// 模仿一般的注解,值为null不做校验,和@NotEmpty一起使用
		if (bool == null) {
			return true;
		}
		// 包含在0和1里面返回true通过,不包含则不通过
		return Arrays.asList("0","1").contains(bool);
	}
}

5、校验模式

  • 默认普通模式
    • 校验完所有的属性,然后返回所有的验证失败信息
  • 快速失败返回模式
    • 只要有一个属性校验失败就立马返回

开启快速失败返回模式

java 复制代码
@Configuration
public class HibernateValidatorConfiguration {
    @Bean
    public Validator validator(){
        ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
                .configure()
                // true  快速失败返回模式    false 普通模式
                .addProperty( "hibernate.validator.fail_fast", "true" )
                .buildValidatorFactory();
        Validator validator = validatorFactory.getValidator();
        return validator;
    }
}

6、全局异常处理

  • get和post参数校验抛出异常类型不一样
java 复制代码
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResultEntity<String> handleBindException(Exception ex) {
        // post请求参数校验
        if (ex instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException exs = (MethodArgumentNotValidException) ex;
            FieldError fieldError = exs.getBindingResult().getFieldError();
            return ResultEntity.fail(400, fieldError.getDefaultMessage(), null);
        }
        // get请求参数校验
        if (ex instanceof ConstraintViolationException) {
            ConstraintViolationException exs = (ConstraintViolationException) ex;
            Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
            for (ConstraintViolation<?> item : violations) {
                return ResultEntity.fail(400, item.getMessage(), null);
            }
        }
        return ResultEntity.fail(500, ex.getMessage(), null);
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class ResultEntity<T> {
    private Integer code;
    private String message;
    private T data;
    public static <T> ResultEntity<T> fail(Integer code, String msg, T t) {
        return new ResultEntity<T>(code, msg, t);
    }
}
相关推荐
艾迪的技术之路13 分钟前
redisson使用lock导致死锁问题
java·后端·面试
qianbo_insist17 分钟前
c++ python 共享内存
开发语言·c++·python
今天背单词了吗98031 分钟前
算法学习笔记:8.Bellman-Ford 算法——从原理到实战,涵盖 LeetCode 与考研 408 例题
java·开发语言·后端·算法·最短路径问题
天天摸鱼的java工程师34 分钟前
使用 Spring Boot 整合高德地图实现路线规划功能
java·后端
东阳马生架构1 小时前
订单初版—2.生单链路中的技术问题说明文档
java
凌览1 小时前
有了 25k Star 的MediaCrawler爬虫库加持,三分钟搞定某红书、某音等平台爬取!
前端·后端·python
这里有鱼汤1 小时前
给你的DeepSeek装上实时行情,让他帮你炒股
后端·python·mcp
咖啡啡不加糖1 小时前
暴力破解漏洞与命令执行漏洞
java·后端·web安全
风象南1 小时前
SpringBoot敏感配置项加密与解密实战
java·spring boot·后端