如何使用自定义 ConstraintValidator 将 ConstraintViolations 添加到索引 i 处的列表元素

Posted

技术标签:

【中文标题】如何使用自定义 ConstraintValidator 将 ConstraintViolations 添加到索引 i 处的列表元素【英文标题】:How to add ConstraintViolations to list element at index i with custom ConstraintValidator 【发布时间】:2019-08-22 18:28:14 【问题描述】:

我目前正在使用自定义ConstraintValidator 来检查具有与其相关联的时间跨度的对象数组,以确定它们的时间跨度是否重叠。验证逻辑正在工作,但是,我不确定如何向每个违反验证逻辑的对象添加“此对象的时间段与另一个对象的时间段重叠”消息。

我尝试了这里描述的几种方法: https://docs.oracle.com/javaee/7/api/javax/validation/ConstraintValidatorContext.html

特别是buildConstraintViolationWithTemplate 方法文档中描述的那些。

以下是代码的相关部分:

@Override
public boolean isValid(List<Shift> shifts, ConstraintValidatorContext context) 

List<Integer> overlappingShiftIndices = determineOverlappingShifts(shifts);

if (!overlappingShiftIndices.isEmpty()) 
  log.debug("Overlap validation failed.");

  context.disableDefaultConstraintViolation();
  // Moving the error from form-level to fields
  for (int index : overlappingShiftIndices) 
    context.buildConstraintViolationWithTemplate("com.generali.standbyscheduler.validation.shiftlist.overlap")
           .addBeanNode()
           .inIterable().atIndex(index)
           .addConstraintViolation();
  

  return false;


log.debug("Overlap validation succeeded.");
return true;

如您所见,我在这里尝试了.addBeanNode().inIterable().atIndex(index) 方法。查看ConstraintViolations 时,属性路径显示为list[index]。这是正确的吗?

我计划使用它从 Thymeleaf 模板中的 BindingResult 访问确定的违规行为,但不确定是否可以通过这种方式访问​​违规行为。该列表将是另一个 bean 的属性,因此我希望使用propertyNameOfList[index] 之类的路径来读取违规行为。或者是propertyNameOfList.list[index] 还是别的什么?

【问题讨论】:

【参考方案1】:

在尝试验证对象列表中的某些字段是否唯一时,我遇到了同样的问题。我自己的解决方案(我在互联网上没有找到:/):

您必须覆盖当前的 PropertyNode 并使用 .addPropertyNode(null).inIterable().atIndex(index) 添加索引号。示例:

约束注解:

@Target(ElementType.FIELD, ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueBusinessIndexValidator.class)
public @interface UniqueEntries 

    String message() default ValidationMessages.REQUIRED_UNIQUE_INDEX;
    Class<?>[] groups() default ;
    Class<? extends Payload>[] payload() default ;

约束验证器:

public class UniqueBusinessIndexValidator implements ConstraintValidator<UniqueEntries, List<HasBusinessIndex>> 

    @Override
    public boolean isValid(List<HasBusinessIndex> collection, ConstraintValidatorContext context) 
        if (collection == null || collection.isEmpty()) 
            return true;
        

        Map<String, List<Integer>> indexesMap = new HashMap<>();
        for (int runner = 0; runner < collection.size(); runner++) 
            String businessIndex = collection.get(runner).getBusinessIndex();
            if (indexesMap.containsKey(businessIndex)) 
                indexesMap.get(businessIndex).add(runner);
             else 
                indexesMap.put(businessIndex, new ArrayList<>(List.of(runner)));
            
        

        boolean isValid = indexesMap.values().stream().noneMatch(indexes -> indexes.size() > 1);
        if (!isValid) 
            indexesMap.values()
                      .stream()
                      .filter(index -> index.size() > 1)
                      .forEach(index -> addUniqueBusinessIndexkennungViolation(context, index));
        
        return isValid;
    

    private void addUniqueBusinessIndexkennungViolation(ConstraintValidatorContext context, List<Integer> indexes) 
        for (Integer index : indexes) 
            context.buildConstraintViolationWithTemplate(ValidationMessages.REQUIRED_UNIQUE_INDEX)
                   .addPropertyNode(null)
                   .inIterable()
                   .atIndex(index)
                   .addPropertyNode("businessIndex")
                   .addConstraintViolation()
                   .disableDefaultConstraintViolation();
        
    

【讨论】:

以上是关于如何使用自定义 ConstraintValidator 将 ConstraintViolations 添加到索引 i 处的列表元素的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 2 个或更多自定义单元格创建自定义 UICollectionView?

如何在shader中使用自定义函数

如何创建自定义 JQuery 函数以及如何使用它?

代码调试篇:如何编写 gdb 自定义命令

代码调试篇:如何编写 gdb 自定义命令

PHP 如何:使用自定义查询基于自定义字段显示帖子