利用JSR-303做数据校验

简单介绍

JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。

  • Bean Validation 中内置的 constraint

内置的constraint

  • Hibernate 中填充一部分

填充部分

如何使用

1. 给Bean添加校验注解

如下给brandId添加了一个NotNull的校验注解,表示这个值不能为空,且可以传message来自定义错误提示,如果没有则为javax中定义的默认提示。

/**
* 品牌id
*/
@NotNull(message = "修改必须指定品牌id")
@TableId
private Long brandId;

2. 开启校验功能@valid

如下是在update接口上增加了一个@Valid标注,表示开启了校验功能,校验到错误以后会有默认的响应。

 /**
 * 修改
 */
@RequestMapping("/update")
//@RequiresPermissions("product:brand:update")
public R update(@Valid @RequestBody BrandEntity brand){
	brandService.updateById(brand);
	return R.ok();
}

3. 获取并处理校验的结果

可以在校验的bean后紧跟一个BindingResult,就可以获得校验的结果,然后对结果中的错误进行处理。如下:

@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
    //如果有错误
    if (result.hasErrors()) {
        Map<String, String> map = new HashMap<>();
        //获取校验的错误结果
        result.getFieldErrors().forEach((item)->{
            //获取错误的属性名
            String fileld = item.getField();
            //获取错误提示
            String message = item.getDefaultMessage();
            map.put(fileld, message);
        });
        //将错误信息封装返回
    	return R.error(400, "提交的数据不合法").put("data", map);
    }else {
        //无错误时处理正常逻辑
    	brandService.save(brand);
        return R.ok();
    }    
}

4. 分组校验

在实际的业务开发场景中,经常有这样的一个场景,对于数据库的同一个字段,有些接口需要校验它不存在,有些接口需要校验它必须存在,典型的如新增和修改接口,对于新增接口往往不需要id(后台自动生成),对于修改接口则要求id必须存在,这种情况下就需要用到分组校验,对不同的接口使用不同的校验标注。

设置分组校验的步骤如下:

  1. 新建两个空的接口AddGroup和UpdateGroup,用来区分添加和更新。
//AddGrup接口
public interface AddGroup {
}
//UpdateGroup接口
public interface UpdateGroup {
}
  1. 在bean中的校验标注中传入指定的分组接口。
//NotNull是在更新的时候起作用
@NotNull(message = "修改必须指定品牌id", groups = {UpdateGroup.class})
//Null是在新建的时候起作用
@Null(message = "新增不能指定品牌id", groups = {AddGroup.class})
//分组可以传多个
//Length的校验则会在修改和更新时都起作用
@Length(min = 0, max = 10000, message = "id必须在一定的范围内", groups = {UpdateGroup.class, AddGroup.class})
@TableId
private Long brandId;
  1. 在controller中的接口中增加@Validated标注
//此接口表示应用的是标记了AddGroup分组的校验标注
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Validated(AddGroup.class) @RequestBody BrandEntity brand){
	brandService.save(brand);
    return R.ok();
}

//此接口表示应用的是标记了UpdateGroup分组的校验标注
@RequestMapping("/update")
//@RequiresPermissions("product:brand:update")
public R update(@Validated(UpdateGroup.class) @RequestBody BrandEntity brand){
	brandService.updateById(brand);
	return R.ok();
}

对于默认没有指定分组的校验注解,在分组校验情况不生效。

5. 自定义校验

对于某些特殊的业务校验场景,系统提供的校验注解无法满足我们的需求,这个时候就需要自定义一些校验。以下将实现一个自定义校验,校验某个字段的取值是否在我们给定的特殊值范围内:

  1. 编写一个自定义的校验注解
@Documented
@Constraint(
        validatedBy = {  }
)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ListValue {
    //此处可在配置文件中定义该检验的默认错误提示信息
    String message() default "{com.atbgpi.common.vaild.NotEmpty.message}";

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

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

    //该处我定义了合理的取值范围
    int[] vals() default {};
}
  1. 编写一个自定义的校验器
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {
    private Set<Integer> set = new HashSet<>();
    //初始化方法
    @Override
    public void initialize(ListValue constraintAnnotation) {
        //这里的vals即为上面定义的校验注解里的vals传值
        int[] vals = constraintAnnotation.vals();
        for (int val: vals) {
            set.add(val);
        }
    }

    /**
     *
     * @param integer 需要校验的值
     * @param constraintValidatorContext
     * @return
     */
    @Override
    public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {
        //校验传入的值是否在最开始设置的vals中
        return set.contains(integer);
    }
}
  1. 关联自定义的校验器和自定义的校验注解
//在第一步定义的校验注解中的@Constraint中指定该校验注解要使用的校验器
@Constraint(
        validatedBy = { ListValueConstraintValidator.class }
)
  1. 使用
//该处使用的是我们自定义的ListValue注解,表示需要showStatus的取值只能为0或1
@ListValue(vals={0, 1}, groups = {UpdateGroup.class, AddGroup.class})
private Integer showStatus;
posted @ 2021-03-21 22:42  bGpi  阅读(21)  评论(0编辑  收藏  举报