从isValid()方法调用更多验证器?

时间:2014-06-25 07:36:11

标签: java bean-validation

假设我有以下情况:

public class EntityA {
   private List<EntityB> listOfBs;
}

我只是在某个验证组下运行时尝试将验证级联到B列表。理想情况下,这个:

public class EntityA {
   @Valid(groups = {SomeSpecificGroup.class})
   private List<EntityB> listOfBs;
}

不幸的是,@ Valid没有groups()属性。所以我想我会尝试类似的东西:

@Constraint(validatedBy = { CascadedValidator.class })
@Target({ ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CascadedValidation {
    Class<?>[] groups() default { };
}

并编写一个验证器(CascadedValidator),激活后将执行级联(==将验证其所放置的集合的所有元素)。 我的问题是如何执行级联验证? 到目前为止我有这个:

public class CascadedValidator implements ConstraintValidator<CascadedValidation, Object>{
    private Class<?>[] groups;

    @Override public void initialize(CascadedValidation constraintAnnotation) {
        groups = constraintAnnotation.groups();
    }

    @Override public boolean isValid(Object value, ConstraintValidatorContext context) {
        if (value == null || !(value instanceof Iterable)) {
            return true;
        }
        for (Object item : (Iterable)value) {
            //validate item using the groups?!
        }
    }
}

我知道我可以通过创建另一个Validator“inline”来实现实际验证:

Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<Object>> violations;
if (decideIfCascade(groups)) {
    for (Object item : (Iterable)value) {
        if (groups!=null && groups.length>0) {
            violations = validator.validate(item, groups);
        } else {
            violations = validator.validate(item);
        }
        if (!violations.isEmpty()) {
            return false;
        }
    }
}
return true;

但这对我来说闻起来很糟糕。 肯定有一种理智/正常/简单的方法吗?

编辑 - 实际用例

我的API接受EntityA(其中包含B列表)和EntityB作为顶级实体(因此您可以直接发送单个B)。 A和B都有一个id属性,但我只需要提交顶级对象的非null id。所以如果服务得到一个带有想法的A和几个“空白”B就可以,但如果我得到一个B作为顶级参数,它必须有一个id。

1 个答案:

答案 0 :(得分:1)

您不应该在ConstraintValidator实施中调用验证引擎。

如果您使用的是Bean Validation 1.1,请查看group conversions,它可以控制在级联验证时传播的验证组。例如。你可以做到以下几点:

@Valid
@ConvertGroup(from = Default.class, to = SomeSpecificGroup.class)
private List<EntityB> listOfBs;