Hibernate Validator 简介
Hibernate Validator是Hibernate项目中的一个数据校验框架,是Bean Validation 的参考实现,Hibernate Validator除了提供了JSR 303规范中所有内置constraint 的实现,还有一些附加的constraint。
Hibernate Validator 作用
- 数据校验逻辑和业务代码分离,程序解耦性提高
- 统一且规范的校验格式,规避了大量重复的数据校验代码
- 精力更加集中于业务代码
Hibernate Validator 使用
项目中,主要通过接口API的接口入参校验和封装工具类在代码中使用两种方式
引入jar包
<!-- 使用SpringBoot框架 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- 直接引用jar包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.9.Final</version>
</dependency>
Java对象添加约束
级联校验需要添加@Valid注解
import com.ai.chinapost.crm.mdb.mgr.common.ValidateGroup;
import lombok.Data;
import org.hibernate.validator.constraints.NotBlank;
import javax.validation.Valid;
import java.util.List;
/**
* @date 2021/01/11
*/
@Data
public class CltMktOccuAndIncmBO {
/**
* 保存或修改标识 1:保存 2:修改
*/
@NotBlank(message = "保存或修改标识不能为空" ,groups = ValidateGroup.CltMktOccuAndIncm.class)
private String saveOrUpdate;
/**
* 更新人编码
*/
@NotBlank(message = "更新人编码必填" ,groups = ValidateGroup.CltMktOccuAndIncm.class)
private String updatedByUserCode;
/**
* 更新人所属机构编码
*/
@NotBlank(message = "更新人所属机构编码必填" ,groups = ValidateGroup.CltMktOccuAndIncm.class)
private String updatedByOrgCode;
/**
* 统计时间
*/
@NotBlank(message = "统计时间必填" ,groups = ValidateGroup.CltMktOccuAndIncm.class)
private String statisDate;
@Valid
private List<CltMktOccuAndIncmDetailBO> cltMktOccuAndIncmDetailList;
}
校验组设置
/**
* @date 2020/7/21
*/
public interface ValidateGroup {
interface CltMktOccuAndIncm {
}
}
API接口入参校验
- 定义接口
接口入参需要添加@Validated注解,进行参数校验
@PostMapping("/saveDist")
@ResponseBody
public ResponseEntity dist(@Validated(ValidateGroup.CltMktOccuAndIncm.class) @RequestBody TrgtMktOcBase entity) throws CommonException {
ResponseEntity resp = distTrgtFacadeConsumer.dist(entity);
return resp;
}
- postMan测试结果
请求报文: { "saveOrUpdate": "", "updatedByUserCode": "20000", "updatedByOrgCode": "100", "statisDate": "", "cltMktOccuAndIncmDetailList": [ { "provId": "", "provName": "安徽" }, { "provId": "370000", "provName": "山东" } ] } 响应报文: { "code": 400, "message": "statisDate:统计时间必填 saveOrUpdate:保存或修改标识不能为空 cltMktOccuAndIncmDetailList[0].provId:省份编码必填 参数值有误" }
封装工具类校验
- 工具类中的Validator对象有两种方式获取(选其一即可)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import javax.validation.Validator;
/**
* @date 2021/01/13
*/
@Component
public class ValidatorConfig {
@Bean(name = "validator")
@Primary
public Validator validator() {
return new LocalValidatorFactoryBean();
}
}
import lombok.Data;
import org.hibernate.validator.HibernateValidator;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
/**
* 校验工具类
*
* @date 2021/01/13
*/
@Component
public class ValidationUtil implements ApplicationContextAware {
private static final ValidationUtil Instance = new ValidationUtil();
public static ValidationUtil getInstance() {
return Instance;
}
private static Validator validator;
// 结合ValidatorConfig类通过Spring容器获取
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ValidationUtil.validator = (Validator) applicationContext.getBean("validator");
}
// 直接通过Validation类获取对象
// 开启快速结束模式 failFast (true)
private static Validator validator1 = Validation.byProvider(HibernateValidator.class).configure().failFast(false).buildValidatorFactory().getValidator();
/**
* 校验对象
*
* @param obj bean对象
* @param groups 校验组,必须是一个接口
* @param <T>
* @return Optional<ValidResult>
*/
public <T> Optional<ValidResult> validateBean(T obj, Class<?>...groups) {
ValidResult result = Instance.new ValidResult();
Set<ConstraintViolation<T>> violationSets = validator.validate(obj, groups);
if (CollectionUtils.isEmpty(violationSets)) {
return Optional.empty();
}
for (ConstraintViolation<T> violation : violationSets) {
result.addError(violation.getPropertyPath().toString(), violation.getMessage());
}
return Optional.of(result);
}
/**
* 校验对象的某一个属性
*
* @param obj bean对象
* @param propertyName 属性名称
* @param <T>
* @return Optional<ValidResult>
*/
public <T> Optional<ValidResult> validateProperty(T obj, String propertyName) {
ValidResult result = Instance.new ValidResult();
Set<ConstraintViolation<T>> violationSets = validator.validateProperty(obj, propertyName);
if (CollectionUtils.isEmpty(violationSets)) {
return Optional.empty();
}
for (ConstraintViolation<T> violation : violationSets) {
result.addError(violation.getPropertyPath().toString(), violation.getMessage());
}
return Optional.of(result);
}
@Data
public class ValidResult {
/**
* 错误信息
*/
private List<ErrorMessage> errors;
public ValidResult() {
this.errors = new ArrayList<>();
}
/**
* 获取所有验证信息
*
* @return 集合形式
*/
public List<ErrorMessage> getAllErrors() {
return errors;
}
/**
* 获取所有验证信息
*
* @return 字符串形式
*/
public String getErrors() {
StringBuilder sb = new StringBuilder();
for (ErrorMessage error : errors) {
sb.append(error.getPropertyPath()).append(":").append(error.getMessage()).append(" ");
}
return sb.toString();
}
public void addError(String propertyName, String message) {
this.errors.add(new ErrorMessage(propertyName, message));
}
}
@Data
private class ErrorMessage {
private String propertyPath;
private String message;
public ErrorMessage() {
}
public ErrorMessage(String propertyPath, String message) {
this.propertyPath = propertyPath;
this.message = message;
}
}
}
- 测试代码
也可不指定分组(groups),会默认使用Default.class分组
public ResponseEntity saveCltMktOccuAndIncm(String params) {
// 校验参数
Class cltMktOccuAndIncm = ValidateGroup.CltMktOccuAndIncm.class;
CltMktOccuAndIncmBO cltMktOccuAndIncmBO = JSON.parseObject(params, CltMktOccuAndIncmBO.class);
Optional<ValidationUtil.ValidResult> validResult = ValidationUtil.getInstance().validateBean(cltMktOccuAndIncmBO, cltMktOccuAndIncm);
if (validResult.isPresent()) {
ValidationUtil.ValidResult errMessage = validResult.get();
return ResponseEntity.fail(ResponseEnum.DATA_ERROR, errMessage.getErrors());
}
return cltMktOccuAndIncmFacade.saveOrModifyCltMktOccuAndIncm(cltMktOccuAndIncmBO);
}
- 测试结果
响应报文: { "code": 400, "message": "statisDate:统计时间必填 saveOrUpdate:保存或修改标识不能为空 cltMktOccuAndIncmDetailList[0].provId:省份编码必填 参数值有误" }
其他常用的constranint
@AssertFalse @AssertTrue 检验boolean类型的值
@DecimalMax @DecimalMin 限定被标注的属性的值的大小
@Digits(intege=,fraction=) 限定被标注的属性的整数位数和小数位数
@Future 检验给定的日期是否比现在晚
@Past 校验给定的日期是否比现在早
@Max 检查被标注的属性的值是否小于等于给定的值
@Min 检查被标注的属性的值是否大于等于给定的值
@NotNull 检验被标注的值不为空
@Null 检验被标注的值为空
@Pattern(regex=,flag=) 检查该字符串是否能够在match指定的情况下被regex定义的正则表达式匹配
@Size(min=,max=) 检查被标注元素的长度
@Valid 递归的对关联的对象进行校验
文章借鉴处