[数据校验/数据质量] 数据校验框架:hibernate-validation

发布时间 2023-11-10 16:54:37作者: 千千寰宇

0 前言

  • 其一,项目中普遍遇到了此问题,故近两天深入地研究了一下。
  • 其二,能够自信地说,仔细看完本篇,就无需再看其他的Java数据校验框架的文章了。

1 数据校验框架概述

1.0 数据校验框架的产生背景

以Web项目为例,用户需要填写表单信息保存提交。
页面输入信息需要进行数据格式校验,并且返回对应的错误提示,以此来达到数据校验的目的,从而避免无效数据被保存或者提交。
这些检查工作包括必填项检查、数值检查、长度检查、身份证号码、手机号码检查等工作。

当请求参数格式不正确的时候,需要程序监测到,对于前后端分离开发过程中,数据校验还需要返回对应的状态码和错误提示信息。

如果将这些字段校验和业务逻辑混合一起写,则会:

  • 代码极其臃肿,且不容易维护;
  • 干扰原有逻辑;

接下来将细述在服务器端,如何对API的数据校验处理技术。

1.1 数据校验框架的演变过程

1.1.1 JSR/Java 规范提案:BeanValidation

  • JSR:Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process) 提出新增一个标准化技术规范的正式请求。

  • 任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准

  • Bean Validation 是一个运行时的数据验证框架的标准,在验证之后验证的错误信息会被马上返回。

    • BeanValidation就是这个JSR规范之一。
    • 提到JSR,相信有小伙伴想去看下到底是个啥。可以看到:
      • 规范从JSR 303JSR 380
      • 目前最新规范是Bean Validation 2.0
    • JSR # Bean Validation : https://jcp.org/en/jsr/summary?id=bean+validation

  • JSR303是专家组成员向JCP提交的第1版Bean Validation,即针对bean数据校验提出的一个规范,使用注解方式实现数据校验。后面有升级版本JSR349JSR380。各个版本的规范对应关系如下:
    • JSR 380(Bean Validation 2.0)
      • JSR380伴随着JAVAEE 8在2017年发布,完全兼容低版本的JAVA SE,Hibernate实现版本6.0.1.Final,Apache BVal实现版本2.0.3(不太确定)
    • JSR 349(Bean Validation 1.1)
      • JSR349伴随着JAVAEE 7在2013年发布,Hibernate实现版本5.1.1.Final,Apache BVal实现版本1.1.2
      • 每一个注解都包含message字段,用于校验失败时作为提示信息,特殊的校验注解,如Pattern(正则校验),还可以自己添加正则表达式。
    • JSR 303(Bean Validation 1.0)
      -JSR303伴随着JAVAEE 6在2009年发布,Hibernate实现版本4.3.1.Final,Apache BVal实现版本0.5

  • 主流 Bean Validation 规范,使用 hibernate-validation 的实现。
    • 如果使用 bean validation 2.0 规范,hibernate-validation 必须选择6.0.1以上版本

Bean Validation 2.0中包含了22个注解

  • JSR / Bean Valiadation 与 Hibernate Validation、Spring Valiadation
    • JSR规定一些校验规范即校验注解,如@Null,@NotNull,@Pattern,位于javax.validation.constraints包下,只提供规范不提供实现。
    • 而hibernate validation是对这个规范的实践,提供相应的实现,并增加一些其他校验注解,如@Email,@Length,@Range等等,位于org.hibernate.validator.constraints包下。
    • spring对hibernate validation进行二次封装,显示校验validated bean时,可以使用spring validation或hibernate validation;
      • 而spring validation另一特性:便是其在springmvc模块中添加自动校验,并将校验信息封装进特定的类中。

1.1.2 JAVAX.VALIDATION.API

  • Java 在2009年的 JAVAEE 6 中发布了 JSR303以及 javax 下的 validation 包内容。
<dependency>  
    <groupId>javax.validation</groupId>  
    <artifactId>validation-api</artifactId>  
    <version>${javax.validation-api.version}</version>  
</dependency>

重要版本
javax.validation:validation-api.version = 2.0.1.Final

spring-boot-starter-web/validation:2.1.4.RELEASE | hibernate-alidation:6.0.16.Final 中使用的 validation-api 为:

javax.validation:validation-api:2.0.1.Final

hibernate-alidation:6.0.16.Final 中使用的 validation-api 为:

javax.validation:validation-api:2.0.1.Final

  • 这项工作的【主要目标】是为java应用程序开发人员提供 :
    • 基于java对象的 约束(constraints)声明
      • 注:每个约束都有参数 message,groups 和 payload。这是 Bean Validation 规范的要求。
    • 对约束的验证工具(validator)
    • 约束元数据存储库查询API
    • 默认实现
  • Java8开始,Java EE 改名为Jakarta EE
    • javax.validation相关的api在jakarta.validation的包下。因此:
      • 大家看不同的版本的时候,会发现以前的版本包javax.validation包下。
      • 最新的版本包在jakarta.validation 包下
    • javase的支持还在jcp(Java Community Process,Java社区标准过程),Java EE 改名 JakartaE E。

<dependency>  
    <groupId>jakarta.validation</groupId>  
    <artifactId>jakarta.validation-api</artifactId>  
    <version>${jakarta.validation-api.version}</version>  
</dependency>

重要版本
jakarta.validation:jakarta.validation-api.version = 2.0.2

spring-boot-starter-validation:2.3.12.RELEASE | hibernate-alidation:6.1.7.Final 使用的 validation-api 为:

jakarta.validation:jakarta.validation-api:2.0.2

1.1.3 HIBERNATE-VALIDATOR

  • hibernate-validator是Hibernate项目中的一个数据校验框架,是Bean Validation参考实现
    • 【注意】
      • 此处的 Hibernate 不是 Hibernate ORM,二者没有任何关系;
      • hibernate-validator 和 hibernate orm 项目 均是 Hibernate 基金会(org.hibernate)下的项目之一。
  • hibernate-validator除了提供了JSR 303规范所有内置constraint 的实现,还有一些附加的constraint
  • 使用hibernate-validator能够将数据校验业务代码中脱离出来,增加代码可读性;同时也让数据校验变得更加方便、简单。
  • 项目官网:
  • 在Java语言中,hibernate-validation已成为【数据校验框架】实质上的标准框架标准实现,再无其他框架可望其项背。

spring-boot-starter-web/validation:2.1.4.RELEASE | hibernate-alidation:6.0.16.Final

hibernate-alidation:6.0.16.Final

spring-boot-starter-validation:2.3.12.RELEASE | hibernate-alidation:6.1.7.Final

2 实践使用

2.1 基本使用步骤

Step1 编写实体类

import lombok.AllArgsConstructor;  
import lombok.Data;  
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
 
    @NotBlank(message = "用户名不能为空")
    private String name;
 
    @NotBlank(message = "邮箱不能为空")
    private String email;
}

Step2 引入依赖包(数据校验的标准框架及实现)

情况1:基于 javax.validation api

基于 javax.validation api 的第三方库的有:
[1] javax.validation:validation-api : 2.0.1.Final

  • [1] hibernate-alidation : 6.0.16.Final
  • [2] spring-boot-starter-web/validation : 2.1.4.RELEASE
    注:本组件依赖的 hibernate-validation 的组件版本为 6.0.16.Final
<!-- | data validation framework | start -->

<!-- javax.validation-api : data validation api design standard & framework -->
<dependency>  
    <groupId>javax.validation</groupId>  
    <artifactId>validation-api</artifactId>  
    <version>${javax.validation-api.version}</version>  
</dependency>
  
<!-- hibernate-validator | http://hibernate.org/validator/documentation-->  
<dependency>  
    <groupId>org.hibernate</groupId>  
    <artifactId>hibernate-validator</artifactId>  
    <version>${hibernate-validator.version}</version>  
</dependency>

<!-- org.glassfish:javax.el | hibernate-validator 依赖此组件,以防报错:"javax.validation.ValidationException: HV000183: Unable to initialize 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead" --><dependency>  
    <groupId>org.glassfish</groupId>  
    <artifactId>javax.el</artifactId>  
    <version>${glassfish.javax.el.version}</version>  
</dependency>

<!-- | data validation framework | end -->
  • 版本变量取值
    • javax.validation-api.version = 2.0.1.Final
    • hibernate-validator.version = 6.0.16.Final
    • glassfish.javax.el.version = 3.0.1-b09

情况2:基于 jakarta.validation api

基于 jakarta.validation api 的第三方库的有:
[1] jakarta.validation : jakarta.validation-api : 2.0.2

  • [1] hibernate-alidation : 6.1.7.Final
  • [2] spring-boot-starter-validation : 2.3.12.RELEASE
    注:本组件依赖的 hibernate-validation 的组件版本为 6.1.7.Final

方式1

<!-- | data validation framework | start -->
<!-- jakarta.validation-api : data validation api design standard & framework -->
<dependency>  
    <groupId>jakarta.validation</groupId>  
    <artifactId>jakarta.validation-api</artifactId>  
    <version>${jakarta.validation-api.version}</version>  
</dependency>
  
<!-- hibernate-validator | http://hibernate.org/validator/documentation -->
<dependency>  
    <groupId>org.hibernate</groupId>  
    <artifactId>hibernate-validator</artifactId>  
    <version>${hibernate-validator.version}</version>  
</dependency>

<!-- org.glassfish:javax.el or org.glassfish:jakarta.el [recommend] (2选1即可) | hibernate-validator 依赖此组件,以防报错:"javax.validation.ValidationException: HV000183: Unable to initialize 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead" -->
<!--
<dependency>  
    <groupId>org.glassfish</groupId>  
    <artifactId>javax.el</artifactId>  
    <version>${glassfish.javax.el.version}</version>  
</dependency> -->
<dependency>  
  <groupId>org.glassfish</groupId>  
  <artifactId>jakarta.el</artifactId>  
  <version>${glassfish.jakarta.el.version}</version>  
</dependency>

<!-- | data validation framework | end -->
  • 版本变量取值
    • jakarta.validation-api.version = 2.0.2
    • hibernate-validator.version = 6.1.7.Final
    • glassfish.javax.el.version = 3.0.1-b09 | glassfish.jakarta.el.version = 3.0.3

方式2:直接引用 spring-boot-starter-validation

<!-- | data validation framework | start -->
<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-validation</artifactId>  
    <version>${spring-boot.version}</version>  
</dependency>
<!-- | data validation framework | end -->

版本变量的取值

spring-boot.version := 2.3.12.RELEASE

由上可见:
[1] spring-boot-starter-validation : 2.3.12.RELEASE
依赖 hibernate-validation : 6.1.7.Final
依赖 org.glasssfish : jakarta.el : 3.0.3

由上可见:
[1] hibernate-validation : 6.1.7.Final
依赖了 org.glasssfish : jakarta.el (由于scopeprovided,故具体版本未限制)

Step3 编写数据验证代码

import javax.validation.ConstraintViolation;  
import javax.validation.Validation;  
import javax.validation.Validator;  
import javax.validation.ValidatorFactory;

//import org.hibernate.validator.HibernateValidator;

import java.util.Set;

public class Test {

	public static void main(String[] args) {  
	    Student student = new Student("小明", null);  
	    System.out.println(student);  
		
		//方式1
	    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();  
		
		//方式2
	    //ValidatorFactory factory = Validation.byProvider( HibernateValidator.class ).configure()  
	    //.addProperty( "hibernate.validator.fail_fast", "true" ) //true 快速失败返回模式 | false 普通模式  
	    //.buildValidatorFactory();  
	  
	    Validator validator = factory.getValidator();  
	    Set<ConstraintViolation<Student>> violations = validator.validate(student);  
	    for (ConstraintViolation<Student> violation : violations) {  
	        System.out.println(violation.getMessage());  
	    }  
	}
}

output

Student(name=小明, email=null)
十一月 10, 2023 12:56:58 下午 org.hibernate.validator.internal.util.Version <clinit>
INFO: HV000001: Hibernate Validator 6.0.16.Final
邮箱不能为空

2.2 支持的POJO校验注解

2.2.1 javax/jakarta.validation 注解列表

在要校验的POJO上加上以下注解即可:

  • 形如:
    • javax.validation.constraints.Email
注解 用途
Valid (最常用的【标识注解】) 递归的对关联的对象进行校验
标记用于验证级联的属性、方法参数或方法返回类型;在验证属性、方法参数或方法返回类型时,将验证在对象及其属性上定义的约束。此行为是递归应用的。
AssertFalse 用于boolean字段,该字段的值只能为false
AssertTrue 用于boolean字段,该字段只能为true
DecimalMax(value) 被注释的元素必须是一个数字,只能大于或等于该值
DecimalMin(value) 被注释的元素必须是一个数字,只能小于或等于该值
Digits(integer,fraction) 检查是否是一种数字的(整数,小数)的位数
Future 检查该字段的日期是否是属于将来的日期
FutureOrPresent 判断日期是否是将来或现在日期
Past 检查该字段的日期是在过去
PastOrPresent 判断日期是否是过去或现在日期
Max(value) 该字段的值只能小于或等于该值
Min(value) 该字段的值只能大于或等于该值
Negative 判断负数
NegativeOrZero 判断负数或0
Positive 判断正数
PositiveOrZero 判断正数或0
NotNull 不能为null
Null 必须为 null
Pattern(value)
@Pattern(regexp = )
被注释的元素必须符合指定的正则表达式
Size(max, min) 检查该字段的size是否在min和max之间,可以是字符串、数组、集合、Map等
Length(max, min) 判断字符串长度
CreditCardNumber 被注释的字符串必须通过Luhn校验算法,银行卡,信用卡等号码一般都用Luhn计算合法性
Email 被注释的元素必须是电子邮箱地址
Length(min=, max=) 被注释的字符串的大小必须在指定的范围内
NotBlank 只能用于字符串不为null,并且字符串trim()以后length要大于0
NotEmpty 集合对象的元素不为0,即集合不为空,也可以用于字符串不为null
Range(min=, max=) 被注释的元素必须在合适的范围内
SafeHtml classpath中要有jsoup包
ScriptAssert 要有Java Scripting API 即JSR 223("Scripting for the JavaTMPlatform")的实现
URL(protocol=,host=,port=,regexp=,flags=) 被注释的字符串必须是一个有效的url
  • 注意
    • @NotEmpty 用在集合类上面
    • @NotBlank 用在String上面
    • @NotNull 用在基本类型上

更多功能,如:自定义校验规则、分组校验、关联参数联合校验请查看官网文档。

2.2.2 springframework.validation 注解列表

  • @Validated(spring) | 最常用的【标识注解】
    • 包路径:
      • org.springframework.validation.annotation.Validated
    • spring 提供的扩展注解,可以方便的用于分组校验
    • 其中,message 是提示消息,groups 可以根据情况来分组。

2.2.3 样例

public class ParamTestDTO implements Serializable {

    private static final long serialVersionUID = 7123882542534668217L;

    @AssertTrue(message = "Error True")
    private Boolean testTrue;

    @AssertFalse(message = "Error False")
    private Boolean testFalse;

    @DecimalMax(value = "10", message = "Error StrMax")
    private String testStrMax;

    @DecimalMin(value = "1", message = "Error StrMin")
    private String testStrMin;

    @Max(value = 10, message = "Error Max")
    private Integer testMax;

    @Min(value = 1, message = "Error Min")
    private Double testMin;

    @Digits(integer = 2, fraction = 3, message = "Error Dig")
    private BigDecimal testDig;

    @Past(message = "Error Past")
    private Date testPast;

    @Future(message = "Error Future")
    private Date testFuture;

    @Null(message = "Error Null")
    private String testNull;

    @NotNull(message = "Error NonNull")
    private String testNonNull;

    @Pattern(regexp = "^[0-9]?[0-9]$", message = "Error Pattern")
    private String testPattern;

    @Size(min = 1, max = 10, message = "Error Size")
    private List<String> testSize;

    @Length(min = 1, max = 10, message = "Error Length")
    private String testLength;

    @NotBlank(message = "Error Blank")
    private String testBlank;

    @NotEmpty(message = "Error NotEmpty")
    private String testEmpty;

    @Range(min = 1, max = 10, message = "Error Range")
    private String testRange;
}

2.3 应用场景

2.3.1 场景:Dubbo中使用Hibernate Validator校验入参

无需util,Dubbo接口配置上的validation为true即可。

  • 在客户端验证参数
<dubbo:reference id="xxxService" interface="xxx.ValidationService" validation="true" />
  • 在服务器端验证参数
<dubbo:service interface="xxx.ValidationService" ref="xxxService" validation="true" />
  • 在代码里校验入参
//obj为包含Hibernate Validator注解的POJO
//快速失败模式
ValidResult validResult = ValidationUtil.fastFailValidate(obj);
//obj为包含Hibernate Validator注解的POJO
//全部校验模式
ValidResult validResult = ValidationUtil.allCheckValidate(obj);

2.3.2 场景:Web POST Api Controller

@RestController
public class StudentController {
	 // ...
	 
    @RequestMapping(value = "/addStudent",method = RequestMethod.POST)
    public String addStudent(@Valid @RequestBody Student student){
        System.out.println("student = [" + student + "]");
        return "ok";
    }
	
	// ...
	
}
  • 注意
    • POST请求必须要加@Valid
      • @Valid注解:递归的对关联的对象进行校验
    • 区分请求参数:@RequestBody 和 @RequestParam
      • @RequestBody 获取的是请求体里面的数据,一般是前端传给后端的JSON字符串。
      • @RequestParam 接收的是url里面的查询参数(比如xxxxxxx?name=admin)

2.3.3 场景:Web GET Api Controller

import org.springframework.validation.annotation.Validated;

@RestController
@Validated
public class StudentController {
	 //...
	 
    @RequestMapping(value = "/addStudent1",method = RequestMethod.GET)
    public String addStudent1(@NotBlank(message = "name不能为空") String name){
        System.out.println("name = [" + name + "]");
        return "ok addStudent1";
    }
    
	//...
}
  • Get请求需要在类上添加 @Validated
    • org.springframework.validation.annotation.Validated

2.3.4 场景:优雅地返回校验信息

2.3.4.1 定义全局异常处理

@ControllerAdvice
public class GlobalExceptionHandler {
 
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ResultEntity handleBindException(MethodArgumentNotValidException ex) {
        FieldError fieldError = ex.getBindingResult().getFieldError();
        // 记录日志。。。
        return ResultEntity.faill(211,fieldError.getDefaultMessage(),null);
    }
    
}

2.3.4.2 定义校验失败返回模板

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResultEntity<T> {
 
    private Integer code;
 
    private String message;
 
    private T data;
 
    public  static <T> ResultEntity<T> faill(Integer code,String msg,T t){
        return new ResultEntity<T>(code,msg,t);
    }
}

2.3.5 场景:对象级联校验

  • Student
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
 
    @NotBlank(message = "用户名不能为空")
    private String name;
 
    @Max(150)
    @Min(10)
    @NotNull(message = "年龄不能为空")
    private Integer age;
 
    @Email
    private String email;
 
    @NotNull(message = "user不能为空")
    @Valid
    private User user;
}
  • User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
 
    private Integer id;
 
    @NotNull(message = "user对象中的username不能为空")
    private String username;
    
}

2.3.6 场景:分组校验

如果同一个类,在不同的使用场景下有不同的校验规则,那么可以使用分组校验。实际需求,如未成年人是不能喝酒,如何校验?

public class Foo {
   @Min(value = 18, groups = {Adult.class})
   private Integer age;
   public interface Adult { }
   public interface Minor { }
}
@RequestMapping("/drink")
public String drink(@Validated({Foo.Adult.class}) Foo foo, BindingResult bindingResult) {
	if (bindingResult.hasErrors()) {
		for (FieldError item : bindingResult.getFieldErrors()) {
		}
		return "fail";
	}
	return "success";
}

2.3.7 场景:自定义校验

作为示例,自定义校验注解@CannotHaveBlank,实现字符串不能包含空格的校验限制:

  • 注解 CannotHaveBlank
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
// 自定义注解中指定这个注解真正的验证者类
@Constraint(validatedBy = {CannotHaveBlankValidator.class})
public @interface CannotHaveBlank {
   // 默认错误消息
   String message() default "不能包含空格";
   // 分组
   Class<?>[] groups() default {};
   // 负载
   Class<? extends Payload>[] payload() default {};
   // 指定多个时使用
   @Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})
   @Retention(RUNTIME)
   @Documented
   @interface List {
       CannotHaveBlank[] value();
   }
}
  • 接口 ConstraintValidator
public interface ConstraintValidator<A extends Annotation, T> {
	void initialize(A constraintAnnotation);// 初始化事件方法
	boolean isValid(T value, ConstraintValidatorContext context);// 判断是否合法
}
  • 实现ConstraintValidator接口完成定制校验逻辑的类 : CannotHaveBlankValidator
// 所有的验证者都需要实现ConstraintValidator接口
public class CannotHaveBlankValidator implements ConstraintValidator<CannotHaveBlank, String> {
	@Override
	public void initialize(CannotHaveBlank constraintAnnotation) {
		//...
	}
	
	@Override
	// ConstraintValidatorContext包含认证中所有的信息,
	// 获取默认错误提示信息,禁用错误提示信息,改写错误提示信息等操作。
	public boolean isValid(String value, ConstraintValidatorContext context) {
	    if (value != null && value.contains(" ")) {
	        //获取默认提示信息
	        String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
	        System.out.println("default message :" + defaultConstraintMessageTemplate);
	        //禁用默认提示信息
	        context.disableDefaultConstraintViolation();
	        //设置提示语
	        context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation();
	        return false;
	    }
	    return true;
	}
}

2.3.8 场景:统一格式化输出

在验证数据时,常常需要给用户告知错误信息。通常情况下,错误信息都是非常简短的。为了更好的告知用户错误信息,validation-api提供了一种非常好的机制来格式化错误信息。

以一个使用validation-api对错误信息进行格式化为例子:

public static  String validateAndFormat(T obj) {
    Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
    Set> constraintViolationSet = validator.validate(obj);
    if (constraintViolationSet != null && constraintViolationSet.size() > 0) {
        StringBuilder sb = new StringBuilder();
        for (ConstraintViolation constraintViolation : constraintViolationSet) {
            sb.append(constraintViolation.getPropertyPath()).append(":").append(constraintViolation.getMessage()).append(",");
        }
        sb.deleteCharAt(sb.length() - 1);
        return sb.toString();
    } else {
        return "";
    }
}

首先使用validator.validate(obj)方法对数据进行验证;
如果有错误信息,则用StringBuilder将错误信息格式化后返回。

2.4 小结:Hibernate-Validator 校验模式

2.4.1 普通模式

默认模式

  • 首先,校验完所有的属性;
  • 然后,返回所有的验证失败信息。

2.4.2 快速失败返回模式

只要有一个失败就立马返回

  • 开启快速失败返回模式
@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;
    }
}

测试验证不通过就会抛出 ConstraintViolationException 异常,和之前普通模式下抛出的异常不一样。
所以,为了格式统一还需要自定义的异常处理

2.4.3 全局异常处理

    // 开启快速失败返回模式,GET请求校验不通过会抛出如下异常,在这对它处理
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public ResultEntity handle(ValidationException exception) {
        if (exception instanceof ConstraintViolationException) {
            ConstraintViolationException exs = (ConstraintViolationException) exception;
 
            Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
            for (ConstraintViolation<?> item : violations) {
                System.out.println(item.getMessage());
                return ResultEntity.faill(212, item.getMessage(), null);
            }
        }
        return ResultEntity.faill(212, "abc", null);
    }

X 参考与推荐文献