假设我有以下情况:
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。
答案 0 :(得分:1)
您不应该在ConstraintValidator
实施中调用验证引擎。
如果您使用的是Bean Validation 1.1,请查看group conversions,它可以控制在级联验证时传播的验证组。例如。你可以做到以下几点:
@Valid
@ConvertGroup(from = Default.class, to = SomeSpecificGroup.class)
private List<EntityB> listOfBs;