Java自定义校验注解实现List、set集合字段唯一性校验

这篇具有很好参考价值的文章主要介绍了Java自定义校验注解实现List、set集合字段唯一性校验。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一: 使用场景

在开发过程中,前端给后端传递集合,并且需要保证集合的实体类中的某些字段必须是惟一的,不能重复。

传递的集合:

private List<User> userInfoList;

集合对应的实体类:

@Data
public class User {

    private int id;

    private String name;
    
    private String card;

}

如果我们要保证传递的name或者card必须是唯一的,不能重复,应该如何实现呢,此时可以通过自定义注解的方式实现。

二: 定义FieldUniqueValid注解

2.1 @FieldUniqueValid


/**
 * 该注解用于校验List集合实体类当中的某些字段的唯一性
 * <p>
 * 条件表达式支持使用"$parent."获取父节点属性(实体内不能使用除List集合外的其他集合类型,例如Set等)
 * <ul>
 * <li>标记在字段上:用于指定单个或多个字段,fields需填写</li>
 * </ul>
 * @author ikun
 * @date 2023.07.27
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FieldUniqueValidator.class)
public @interface FieldUniqueValid {

    //需要进行唯一校验的字段
    String[] fieldsCode() default{};

    String[] fieldsName() default{};

    String message() default "[fieldName]列[index]行数据重复";

    //不能有默认值,报错:contains Constraint annotation, but the groups parameter default value is not the empty array
    Class<?>[] groups() default {};//在那种组中使用

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

2.2 注解说明

@Documented

@Document 是 java 在生成文档,是否显示注解的开关。

@Target(ElementType.FIELD)

ElementType.FIELD:该注解只能声明在一个类的字段前。

2.3 @Constraint 注解介绍

@Constraint注解是Java Bean Validation框架中的一个注解,用于自定义约束注解,即自定义校验规则。

通过在自定义注解上添加@Constraint注解,可以将该注解标记为一个自定义约束注解。同时,需要指定一个实现了ConstraintValidator接口的验证器类,用于验证该注解所标记的字段或参数是否符合自定义的校验规则。

@Constraint注解有以下属性:

  • validatedBy:用于指定实现了ConstraintValidator接口的验证器类。该属性的值是一个Class对象数组,可以指定多个验证器类。

  • message:用于指定当校验失败时,所返回的错误信息。可以使用占位符{},在校验器中使用具体的参数替换。

  • groups:用于指定分组,即根据不同的分组应用不同的校验规则。

  • payload:用于指定元数据,即可以通过该属性传递一些额外的验证信息。

使用@Constraint注解,可以通过自定义注解的方式,为字段或参数添加自定义的校验规则,并实现校验逻辑。这样,在进行参数校验时,可以方便地通过注解的方式来调用自定义的校验规则。

2.4 @FieldUniqueValid注解使用

@FieldUniqueValid(fieldsCode = {"name,card"}, fieldsName = {"姓名,身份证号"})
private List<User> userInfoList;
  • fieldsCode :需要校验的字段
  • fieldsName :校验字段对应的名称

三:自定义FieldUniqueValidator校验类

@Slf4j
public class FieldUniqueValidator implements ConstraintValidator<FieldUniqueValid, Iterable<?>> {

    private String[] fieldsCode;

    private String[] fieldsName;

    /**
     * 数据有重复的字段名称
     */
    private static final String FIELD_NAME= "[fieldName]";

    /**
     * 相同元素下标集合
     */
    private static final String INDEX = "[index]";

    /**
     * 初始化参数
     * @param constraintAnnotation 注解的值
     */
    @Override
    public void initialize(FieldUniqueValid constraintAnnotation) {
        fieldsCode = constraintAnnotation.fieldsCode();
        fieldsName = constraintAnnotation.fieldsName();
    }

    @Override
    public boolean isValid(Iterable<?> value, ConstraintValidatorContext context) {
        //如果没有配置校验字段信息,则直接通过
        if(fieldsCode.length == 0 && fieldsName.length == 0){
            return true;
        }
        if(fieldsCode.length != fieldsName.length){
            throw new RuntimeException("@FieldUniqueValid注解所对应的fieldsCode和fieldsName无法相互映射");
        }
        if(value == null){
            throw new RuntimeException("@FieldUniqueValid注解所在的集合为空,无法判断");
        }
        for (int i = 0; i < fieldsCode.length; i++) {
            List<Object> list = new ArrayList<>();
            Iterator<?> iterator = value.iterator();
            long index;
            for (index = 0; iterator.hasNext(); index++) {
                Object fieldValue = null;
                try {
                    //forceAccess = true,属性是私有的,需要设置为true打开权限
                    fieldValue = FieldUtils.readField(iterator.next(),fieldsCode[i],true);
                } catch (IllegalAccessException e) {
                    log.error(fieldsName[i] + "转化失败,无法进行校验", e);
                }
                list.add(fieldValue);
            }
            //去重后的总数
            long count = list.stream().distinct().count();
            //去重之前和去重以后进行比较
            if(count < index){
                //返回重复元素下标集合
                String sameIndex = getListSameIndex(list);
                String defaultConstraintViolation = context.getDefaultConstraintMessageTemplate();
                context.disableDefaultConstraintViolation();
                String convertedConstraintViolation = defaultConstraintViolation.replace(FIELD_NAME, fieldsName[i]).replace(INDEX, sameIndex);
                context.buildConstraintViolationWithTemplate(convertedConstraintViolation).addConstraintViolation();
                return false;
            }
        }
        return true;
    }


}

3.1 实现ConstraintValidator

ConstraintValidator<FieldUniqueValid, Iterable<?>>:

  • FieldUniqueValid:需要校验的注解,就是我们自定义的
  • Iterable<?>:前端传递list的类型,此时用Iterable是因为数据支持list和set集合

3.2 重写initialize方法

可以从onstraintAnnotation参数中获取fieldsCode、fieldsName里面的参数。主要作用就是将注解的参数进行初始化

3.3 重写isValid方法

Iterable<?> value, ConstraintValidatorContext context

  • value:可以获取到传递的集合数据
  • context:获取注解上的message信息

3.4 获取list集合重复数据的下标

/**
     * 集合【List】找出list中重复元素的下标(显示下标所在位置)
     * @param list
     */
    public static String getListSameIndex(List<?> list){
        List<Object> same = new ArrayList<>();
        List<?> collect = list.stream().distinct().collect(Collectors.toList());
        if(collect.size() == list.size()){
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i <collect.size(); i++) {
            for (int j = 0; j < list.size(); j++) {
                if (list.get(j).equals(collect.get(i))){
                    same.add(j+1);
                }
            }
            if (same.size() > 1){
                sb.append(same).append("、");
            }
            same.clear();
        }
        return sb.substring(0, sb.toString().lastIndexOf("、"));
    }

3.5 思路

首先获取到集合的数据,然后通过反射,用循环遍历获取到name字段的list数据,然后去重。将去重前后的list进行比较。如果长度变化了则说明有重复数据。此时返回false。然后我们我们通过getListSameIndex方法获取到list重复数据的下标然后替换[index]。

3.6 测试

3.6.1 前端传递参数,需要进行唯一性校验的字段

Java自定义校验注解实现List、set集合字段唯一性校验,java,list,开发语言

3.6.2 message提示

Java自定义校验注解实现List、set集合字段唯一性校验,java,list,开发语言文章来源地址https://www.toymoban.com/news/detail-627575.html

到了这里,关于Java自定义校验注解实现List、set集合字段唯一性校验的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java集合(List、Set、Map)

    Java中的集合是用于存储和组织对象的数据结构。Java提供了许多不同的集合类,包括List、Set和Map等,以满足不同的需求。下面将介绍一些常见的Java集合类及其使用方法。 一、List List是一个有序的集合,它允许元素重复出现,并提供了索引访问元素的功能。List可以通过以下方

    2024年02月16日
    浏览(41)
  • Java-集合框架-List,Set,Map,队列

    Java集合框架是一组用于存储和操作数据的类和接口。它提供了不同类型的集合,如List,Set,Map和队列,以满足不同的需求。 List:有序的集合,允许重复的元素。 Set:无序的集合,不允许重复的元素。 Map:键值对的集合,每个元素都包含一个键和一个值。 队列:先进先出(

    2024年02月11日
    浏览(49)
  • Java的集合类:List、Set、Map

    在 Java 中,集合类是一组有序或无序的数据元素的集合。Java 集合类可用于存储和操作各种数据类型的元素,如整数、字符串、对象等。集合类是动态的,可以在运行时根据需要调整其大小。 Java 集合类可以分为三类: List - 有序集合,允许重复元素 Set - 无序集合,不允许重

    2024年02月16日
    浏览(48)
  • Java list集合中根据其中两个字段去重

    可以使用Java 8的Stream API和Collectors.toSet()方法来实现根据其中两个字段去重。 首先,使用Stream API将List转换为Stream,然后使用distinct()方法进行去重。distinct()方法默认使用元素的equals()和hashCode()方法进行去重。如果想要根据其中两个字段进行去重,需要重写equals()和hashCode()方法

    2024年02月16日
    浏览(62)
  • java 对List集合中元素对象按字段分组,并收集指定字段的值

    一、实现:对已有对象集合ListPersion ,需要获取Persion对象的字段 name分组, 并对年龄age字段值做收集 二、字段分组收集方法  注:由于实际业务只有String类型跟数字类型,所以只对String跟Object两种类型判空 三、测试代码 四、结果

    2024年02月15日
    浏览(50)
  • Java基础六 - Collection集合List、Set、Queue,Map

    1. List - ArrayList、LinkedList、Vector ArrayList         2. LinkedList         3. Vector         4. 常见使用方法 2. Set - HashSet、LinkedHashSet、TreeSet 1. HashSet 2. LinkedHashSet 3. TreeSet 4. 常用方法 3. Map - HashMap、TreeMap、LinkedHashMap、Hashtable 1. HashMap 2. LinkedHashMap 3. TreeMap 4. Hashtable 5.

    2024年02月14日
    浏览(49)
  • Java中List对象集合按照对象中某字段进行排序

    在Java中,可以使用List集合对象的sort(Comparator? super E c)方法,按照对象中某字段对List集合进行排序 栗子: 打印输出结果如下:

    2024年02月12日
    浏览(56)
  • 【Java 集合框架API接口】Collection,List,Set,Map,Queue,Deque

    博主: _LJaXi Or 東方幻想郷 专栏: Java | 从跨行业到跨平台 开发工具: IntelliJ IDEA 2021.1.3 Java集合API提供了一组功能强大的数据结构和算法, 具有以下作用( 简述 ) 存储和组织数据 提供高效的数据访问和操作 实现算法和数据处理 提供线程安全性 支持泛型编程 java.util.Collection

    2024年02月12日
    浏览(48)
  • Java02-迭代器,数据结构,List,Set ,TreeSet集合,Collections工具类

    目录 什么是遍历? 一、Collection集合的遍历方式 1.迭代器遍历 方法 流程 案例 2. foreach(增强for循环)遍历 案例 3.Lamdba表达式遍历 案例 二、数据结构 数据结构介绍 常见数据结构 栈(Stack) 队列(Queue) 链表(Link) 散列表(Hash Table) 树(Tree) List接口 ArraysList集合 Linked

    2024年02月14日
    浏览(47)
  • 【JAVA】集合与背后的逻辑框架,包装类,List,Map,Set,静态内部类

    ❤️ Author: 老九 ☕️ 个人博客:老九的CSDN博客 🙏 个人名言:不可控之事 乐观面对 😍 系列专栏: Collection 接口,在 Java 当中,Collection 也是重要的数据结构。 在创建 Collection 的时候,要 通过 new 来使用。但是查看 Collection 源码的时候, 发现 Collecting 是一个接口

    2024年02月07日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包