解决多字段联合逻辑校验问题【享学Spring MVC】

每篇一句

> 不要像祥林嫂一样,天天抱怨着生活,日日思考着辞职。得罪点说一句:“沦落”到要跟这样的人共事工作,难道自己身上就没有原因?

前言

本以为洋洋洒洒的把Java/Spring数据(绑定)校验这块说了这么多,基本已经算完结了。但今天中午一位热心小伙伴在使用Bean Validation做数据校验时上遇到了一个稍显特殊的case,由于此校验场景也比较常见,因此便有了本文对数据校验补充。

关于Java/Spring中的数据校验,我有理由坚信你肯定遇到过这样的场景需求:在对JavaBean进行校验时,b属性的校验逻辑是依赖于a属性的值的;换个具象的例子说:当且仅当属性a的值=xxx时,属性b的校验逻辑才生效。这也就是我们常说的多字段联合校验逻辑~ 因为这个校验的case比较常见,因此促使了我记录本文的动力,因为它会变得有意义和有价值。当然对此问题有的小伙伴说可以自己用if else来处理呀,也不是很麻烦。本文的目的还是希望对数据校验一以贯之的做到更清爽、更优雅、更好扩展而努力。 > 需要有一点坚持:既然用了Bean Validation去简化校验,那就(最好)不要用得四不像,遇到问题就解决问题~

热心网友问题描述

为了更真实的还原问题场景,我贴上聊天截图如下: 在这里插入图片描述 待校验的请求JavaBean如下: 在这里插入图片描述 校需求描述简述如下: 在这里插入图片描述 这位网友描述的真实生产场景问题,这也是本文讲解的内容所在。 虽然这是在Spring MVC条件的下使用的数据校验,但按照我的习惯为了更方便的说明问题,我会把此部分功能单摘出来,说清楚了方案和原理,再去实施解决问题本身(文末)~

方案和原理

对于单字段的校验、级联属性校验等,通过阅读我的系列文章,我有理由相信小伙伴们都能驾轻就熟了的。本文给出一个最简单的例子简单"复习"一下:

@Getter
@Setter
@ToString
public class Person {

    @NotNull
    private String name;
    @NotNull
    @Range(min = 10, max = 40)
    private Integer age;

	@NotNull
    @Size(min = 3, max = 5)
    private List<string> hobbies;

    // 级联校验
    @Valid
    @NotNull
    private Child child;
}

测试:

public static void main(String[] args)  {
    Person person = new Person();
    person.setName("fsx");
    person.setAge(5);
	person.setHobbies(Arrays.asList("足球","篮球"));
    person.setChild(new Child());

    Set<constraintviolation<person>&gt; result = Validation.buildDefaultValidatorFactory().getValidator().validate(person);

    // 对结果进行遍历输出
    result.stream().map(v -&gt; v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue()).forEach(System.out::println);
}

运行,打印输出:

child.name 不能为null: null
age 需要在10和40之间: 5
hobbies 个数必须在3和5之间: [足球,篮球]

结果符合预期,(级联)校验生效。 > 通过使用@Valid可以实现递归验证,因此可以标注在List上,对它里面的每个对象都执行校验


问题来了,针对上例,现在我有如下需求:

  1. 若20 <= age < 30,那么hobbiessize需介于1和2之间
  2. 若30 <= age < 40,那么hobbiessize需介于3和5之间
  3. age其余值,hobbies无校验逻辑
实现方案

Hibernate Validator提供了非标准@GroupSequenceProvider注解。本功能提供根据当前对象实例的状态,动态来决定加载那些校验组进入默认校验组。

为了实现上面的需求达到目的,我们需要借助Hibernate Validation提供给我们的DefaultGroupSequenceProvider接口来处理。

// 该接口定义了:动态Group序列的协定
// 要想它生效,需要在T上标注@GroupSequenceProvider注解并且指定此类为处理类
// 如果`Default`组对T进行验证,则实际验证的实例将传递给此类以确定默认组序列(这句话特别重要  下面用例子解释)
public interface DefaultGroupSequenceProvider<t> {
	// 合格方法是给T返回默认的组(多个)。因为默认的组是Default嘛~~~通过它可以自定指定
	// 入参T object允许在验证值状态的函数中动态组合默认组序列。(非常强大)
	// object是待校验的Bean。它可以为null哦~(Validator#validateValue的时候可以为null)

	// 返回值表示默认组序列的List。它的效果同@GroupSequence定义组序列,尤其是列表List必须包含类型T
	List<class<?>&gt; getValidationGroups(T object);
}

注意:

  1. 此接口Hibernate并没有提供实现
  2. 若你实现请必须提供一个空的构造函数以及保证是线程安全的

按步骤解决多字段组合验证的逻辑: 1、自己实现DefaultGroupSequenceProvider接口(处理Person这个Bean)

public class PersonGroupSequenceProvider implements DefaultGroupSequenceProvider<person> {

    @Override
    public List<class<?>&gt; getValidationGroups(Person bean) {
        List<class<?>&gt; defaultGroupSequence = new ArrayList&lt;&gt;();
        defaultGroupSequence.add(Person.class); // 这一步不能省,否则Default分组都不会执行了,会抛错的

        if (bean != null) { // 这块判空请务必要做
            Integer age = bean.getAge();
            System.err.println("年龄为:"
  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值