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

文章目录

  • 一: 使用场景
  • 二: 定义FieldUniqueValid注解
    • 2.1 @FieldUniqueValid
    • 2.2 注解说明
    • 2.3 @Constraint 注解介绍
    • 2.4 @FieldUniqueValid注解使用
  • 三:自定义FieldUniqueValidator校验类
    • 3.1 实现ConstraintValidator
    • 3.2 重写initialize方法
    • 3.3 重写isValid方法
    • 3.4 获取list集合重复数据的下标
    • 3.5 思路
    • 3.6 测试
      • 3.6.1 前端传递参数,需要进行唯一性校验的字段
      • 3.6.2 message提示

一: 使用场景

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

传递的集合:

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 arrayClass<?>[] 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 注解的值*/@Overridepublic void initialize(FieldUniqueValid constraintAnnotation) {fieldsCode = constraintAnnotation.fieldsCode();fieldsName = constraintAnnotation.fieldsName();}@Overridepublic 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 前端传递参数,需要进行唯一性校验的字段

![在这里插入图片描述](https://img-blog.csdnimg.cn/e613f37d22554914940b3a162ed0916c.png

3.6.2 message提示

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/22749.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32(HAL)多串口进行重定向(printf函数发送数据)

目录 1、简介 2.1 基础配置 2.1.1 SYS配置 2.1.2 RCC配置 2.2 串口外设配置 2.3 项目生成 3、KEIL端程序整合 4、效果测试 1、简介 在HAL库中&#xff0c;常用的printf函数是无法使用的。本文通过重映射实现在HAL库多个串口可进行类似printf函数的操作。 2.1 基础配置 2.…

【c++】VSCode配置 c++ 环境(重新制作)

上一篇帖子【c】VSCode配置 c 环境&#xff08;小白教程&#xff09;_vscode配置c/c环境_StudyWinter的博客-CSDN博客 大火&#xff0c;但是依旧有很多小伙伴反应没有配好环境&#xff0c;今天打算重新写一个教程&#xff0c;希望对大家有帮助。 1 MinGW下载安装 在CSDN上传了…

实现邮箱管理之gmail邮箱、office365(Azure)邮箱之披荆斩棘问题一览

要进行Office365邮箱的授权对接&#xff0c;你需要先申请一个应用&#xff0c;并获取授权访问令牌。 以下是一个简单的步骤&#xff1a; 登录 Azure 门户&#xff1a;https://portal.azure.com/创建一个新的应用程序&#xff0c;或者使用现有的应用程序。要创建新的应用程序&…

React Native从文本内容尾部截取显示省略号

<Textstyle{styles.mMeNickname}ellipsizeMode"tail"numberOfLines{1}>{userInfo.nickname}</Text> 参考链接&#xff1a; https://www.reactnative.cn/docs/text#ellipsizemode https://chat.xutongbao.top/

【ASP.NET MVC】使用动软(一)(9)

一、解决的问题 前文为解决数据库操作设计的 TestMysql 类&#xff0c;仅简单地封装了一个Query函数&#xff0c;代码如下&#xff1a; public class TestMysql{public static string SqlserverConnectStr "server127.0.0.1;charsetutf8;user idroot;persistsecurityin…

数据库数据恢复-Oracle数据库文件出现坏块的数据恢复案例

Oracle数据库故障&初检&分析&#xff1a; 打开Oracle数据库时报错&#xff0c;报错信息&#xff1a;“system01.dbf需要更多的恢复来保持一致性&#xff0c;数据库无法打开”。用户急需恢复zxfg用户下的数据。 出现上述报错的可能原因包括&#xff1a;控制文件损坏、数…

C高级--day3(shell中的输入、命令置换符、数组、算数运算、分支结构)

#!/bin/bash pls ~/ -l | grep "^-" | wc -l qls ~/ -l | grep "^d" | wc -l echo "普通文件个数&#xff1a;$p" echo "目录文件个数&#xff1a;$q"#!/bin/bash read file posexpr index $file \. strexpr substr $file $((pos1)) 2…

视频是如何做成gif动图的?1分钟快速转gif动画

常见的电影、电视剧等视频体积较大不易于传输和保存。为了方便大家使用可以将视频制作成GIF&#xff0c;可直接发送给对方非常的方便。那么&#xff0c;要怎么将视频转换成gif动画呢&#xff1f;很简单&#xff0c;使用专业的gif图片在线制作工具–GIF中文网&#xff0c;无需下…

企业电子招投标采购系统java spring cloud+spring boot功能模块功能描述+数字化采购管理 采购招投标

​功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为外…

【IDEA】常用插件清单

【IDEA】常用插件清单 arthas ideaCodeium: AI Autocomplete for xxxCommit-MessageGenerateAllSetterMaven HelperMybatisPlusOne Dark themePDF ViewerRainbow BracketsRestfulToolSequenceDiagramSonarLintTranslation arthas idea 快捷生成arthas命令 Codeium: AI Autoc…

微信云托管(本地调试)⑥:nginx、vue刷新404问题

一、nginx默认路径 1.1、默认配置文件路径&#xff1a;/etc/nginx/nginx.conf 1.2、默认资源路径&#xff1a;/usr/share/nginx/html/index.html 二、修改nginx.conf配置 &#xff08;注意配置中的&#xff1a;include /etc/nginx/conf.d/*.conf; 里面包了一个server配置文件…

【flink】开启savepoint

先启动一个任务 flink run -c com.yang.flink.CDCJob test-cdc.jar开启savepoint 命令&#xff1a; flink savepoint JobID 文件地址 flink savepoint e929a11d79bdc5e6f140f2cfb92e1335 file:///workspace/flinkSavepoints/backend这样就开启好了 操作中的错误 详细信…

Day10-作业(SpringBootWeb案例)

作业1&#xff1a;完成课上预留给大家自己完成的功能 【部门管理的修改功能】 注意&#xff1a; 部门管理的修改功能&#xff0c;需要开发两个接口&#xff1a; 先开发根据ID查询部门信息的接口&#xff0c;该接口用户查询数据并展示 。(一定一定先做这个功能) 再开发根据ID…

【websocket - Tornado】简易聊天应用

1、背景 项目测试的过程中需要自己搭建一个webscoket站点,确保此类服务接入后台系统后访问不受影响。python的服务框架常用的有Flask、Django、Tornado,每个框架的侧重点不同,导致使用的场景就会有所差异。 Flask轻量级,采用常规的同步编程方式,需要安装其他模块辅助,主…

逆向时如何找到MingGW(GNU)编译程序的main函数

编译器是MingGW生成的可执行文件的显著特点是, 最终运行ZwContinue后程序就莫名其妙启动了, 也找不到main函数。 为了探究里面究竟怎么回事, 我找到了wrk-v1.2的源码, 其中包含了ZwContinue的实现, 首先先看一下注释, API界面包含了2个参数, 其中让人感兴趣的是PCONTEXT, 这是…

【MySQL】仓储--维护出入库流水、库存,去重数量逻辑修正

系列文章 C#底层库–MySQLBuilder脚本构建类&#xff08;select、insert、update、in、带条件的SQL自动生成&#xff09; 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/129179216 C#底层库–MySQL数据库操作辅助类&#xff08;推荐阅读&#xff0…

数据结构【第3章】——线性表

线性表的定义 线性表&#xff1a;零个或多个数据元素的有限序列。 1&#xff09;线性表是一个序列。即元素之间是有顺序的&#xff0c;若元素存在多个&#xff0c;则第一个元素无前驱&#xff0c;最后一个元素无后继&#xff0c;其他每个元素都有且只有一个前驱和后继。 2&a…

GitHub上删除项目后,IDEA分享项目到GitHub提示Remote is already on GitHub

文章目录 一、错误信息二、解决方法1.删除GitHub上的项目2.找到项目里的.git隐藏文件3.找到config文件4.打开config文件&#xff0c;删除[remote "git-test"]及下面两行内容5.继续使用IDEA分享项目到GitHub即可 一、错误信息 二、解决方法 1.删除GitHub上的项目 2.…

MacBook Pro 16 M1 Max 升级 macOS Ventura 13.5 兼容测评

今天给大家带来了 MacBook Pro 16 M1 Max 升级 macOS Ventura 13.5 兼容 100 挑战赛 的视频&#xff0c;现在充电头再以文章的形式呈现给大家&#xff0c;让大家更清楚、直白的了解这款笔记本在升级系统后的兼容性如何。 MacBook Pro 16 M1 Max 配置了 140W 的 MagSafe 充电口&…

HCIP-Cloud Service V3.0 真题和机构资料

通过认证验证的能力 具备传统企业应用架构和云原生架构设计上云的能力 建议掌握的知识 对IT相关技术有基本的了解&#xff1b;具备一定的公有云服务基础知识&#xff1b;对云计算、网络、存储、数据库等知识有一定的了解&#xff0c;具备Linux操作系统的基础知识 机构的考试大…