业务场景

在项目中,某些情景下我们需要验证编码是否重复,账号是否重复,身份证号是否重复等...

那么有没有办法可以解决这类似的重复代码量呢?

我们可以通过自定义注解校验的方式去实现,在实体类上面加上自定义的注解即可

@FieldRepeat(fields = {"account"}, message = "账号已存在!")

实现步骤

1.引入依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.3.11.RELEASE</version>
</dependency>

2.新增自定义注解

// 注意:实体类必须继承Model,且需要标明表名@TableName("t_test"),校验字段需要加上注解 @TableField("name")
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FieldRepeatValidator.class)
public @interface FieldRepeat { /**
* 需要检验的字段
* @return
*/
String[] field() default {}; String message() default "您输入的内容已存在"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { };
}

3.新增@FieldRepeat注解接口实现类

public class FieldRepeatValidator implements ConstraintValidator<FieldRepeat, Object> {

    @Autowired
private FieldRepeatUtils fieldRepeatUtils; private String[] fields;
private String message; @Override
public void initialize(FieldRepeat fieldRepeat) {
this.fields = fieldRepeat.fields();
this.message = fieldRepeat.message();
} @Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
return fieldRepeatUtils.fieldRepeat(fields, o, message);
}
}

4.新增字段内容重复判断处理工具类

@Component
@Slf4j
public class FieldRepeatUtils {
/**
* 实体类中的id字段
*/
private String idColumnName;
/**
* 实体类中的id的值
*/
private Object idColumnValue; /**
*
* @param fields 验证的字段数组
* @param message 如果不满足返回的消息
* @param o 实体类
* @return
*/
public boolean fieldRepeat(String [] fields,String message,Object o) throws ValidationException,IllegalAccessException {
try {
// 没有校验的值返回true
if(fields != null && fields.length == 0){
return true;
}
checkUpdateOrSave(o);
checkRepeat(fields,o,message);
return true;
}catch (ValidationException ed){
throw new ValidationException(message);
}catch (IllegalAccessException e){
throw new IllegalAccessException(e.getMessage());
}
}
/**
* 通过传入的实体类中 @TableId 注解的值是否为空,来判断是更新还是保存
* 将值id值和id列名赋值
* id的值不为空 是更新 否则是插入
* @param o 被注解修饰过的实体类
* @return
*/
public void checkUpdateOrSave(Object o) throws IllegalAccessException{
Field[] fields = getAllFields(o.getClass());
for (Field f:fields) {
// 设置私有属性可读
f.setAccessible(true);
if(f.isAnnotationPresent(TableId.class)){
TableId tableId = f.getAnnotation(TableId.class);
idColumnName = tableId.value();
idColumnValue = f.get(o);
}
}
}
/**
* 获取本类及其父类的属性的方法
* @param clazz 当前类对象
* @return 字段数组
*/
private static Field[] getAllFields(Class<?> clazz) {
List<Field> fieldList = new ArrayList<>();
while (clazz != null){
fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
clazz = clazz.getSuperclass();
}
Field[] fields = new Field[fieldList.size()];
return fieldList.toArray(fields);
}
/**
* 通过传入的字段值获取数据是否重复
* @param fields
* @param o
* @param message
* @return
*/
public void checkRepeat(String [] fields,Object o,String message) throws ValidationException, IllegalAccessException {
Model model = (Model) o;
//Mybatis-plus 3.0以下用EntityWrapper
QueryWrapper<Object> qw = new QueryWrapper<>();
Map<String,Object> queryMap = getColumns(fields,o);
for (Map.Entry<String, Object> entry : queryMap.entrySet()) {
qw.eq(entry.getKey(), entry.getValue());
}
if(idColumnValue != null){
//更新的话,那条件就要排除自身
qw.ne(idColumnName,idColumnValue);
}
List list = model.selectList(qw);
if(list != null && list.size()>0){
throw new ValidationException(message);
}
} /**
* 多条件判断唯一性,将我们的属性和值组装在map中,方便后续拼接条件
* @param fields
* @param o
* @return
*/
public Map<String,Object> getColumns(String [] fields,Object o) throws IllegalAccessException {
Field[] fieldList = getAllFields(o.getClass());
Map<String,Object> map = new HashMap<>();
for (Field f : fieldList) {
// ② 设置对象中成员 属性private为可读
f.setAccessible(true);
// 判断字段是否包含在数组中,如果存在,则将它对应的列字段放入map中
if(ArrayUtils.contains(fields,f.getName())){
getMapData(map,f,o);
}
}
return map;
}
/**
* 得到查询条件
* @param map 列字段
* @param f 字段
* @param o 传入的对象
*/
private void getMapData( Map<String,Object> map,Field f,Object o) throws IllegalAccessException {
try {
if(f.isAnnotationPresent(TableField.class)){
TableField tableField = f.getAnnotation(TableField.class);
Object val = f.get(o);
map.put(tableField.value(),val);
}
}catch (IllegalAccessException i){
throw new IllegalAccessException("获取字段的值错误");
}
}
}

测试类

@Data
@TableName("t_test")
@FieldRepeat(fields = {"name"},message = "名称不能重复,请重新输入",groups = {Base.Save.class,Base.Update.class})
public class Test extends Model<Test>{ @TableId(value = "id", type = IdType.AUTO)
@NotNull(message = "id不能为空",groups = {Base.Update.class,Base.Delete.class,Base.Detail.class})
private Long id; @TableField("name")
@NotNull(message = "名称不能为空",groups = {Base.Update.class,Base.Save.class})
private String name;
}

Java 自定义注解校验字段唯一性的更多相关文章

  1. Java自定义注解源码+原理解释(使用Java自定义注解校验bean传入参数合法性)

    Java自定义注解源码+原理解释(使用Java自定义注解校验bean传入参数合法性) 前言:由于前段时间忙于写接口,在接口中需要做很多的参数校验,本着简洁.高效的原则,便写了这个小工具供自己使用(内容 ...

  2. Java 自定义注解 校验指定字段对应数据库内容重复

    一.前言 在项目中,某些情景下我们需要验证编码是否重复,账号是否重复,身份证号是否重复等... 而像验证这类代码如下: 那么有没有办法可以解决这类似的重复代码量呢? 我们可以通过自定义注解校验的方式去 ...

  3. java自定义注解实现前后台参数校验

    2016.07.26 qq:992591601,欢迎交流 首先介绍些基本概念: Annotations(also known as metadata)provide a formalized way ...

  4. java自定义注解类

    一.前言 今天阅读帆哥代码的时候,看到了之前没有见过的新东西, 比如java自定义注解类,如何获取注解,如何反射内部类,this$0是什么意思? 于是乎,学习并整理了一下. 二.代码示例 import ...

  5. java自定义注解注解方法、类、属性等等【转】

    http://anole1982.iteye.com/blog/1450421 http://www.open-open.com/doc/view/51fe76de67214563b20b385320 ...

  6. JAVA自定义注解 ------ Annotation

    日常开发工作中,合理的使用注解,可以简化代码编写以及使代码结构更加简单,下面记录下,JAVA自定义注解的开发过程. 定义注解声明类. 编写注解处理器(主要起作用部分). 使用注解. 相关知识点介绍, ...

  7. Java自定义注解和运行时靠反射获取注解

    转载:http://blog.csdn.net/bao19901210/article/details/17201173/ java自定义注解 Java注解是附加在代码中的一些元信息,用于一些工具在编 ...

  8. java自定义注解知识实例及SSH框架下,拦截器中无法获得java注解属性值的问题

    一.java自定义注解相关知识 注解这东西是java语言本身就带有的功能特点,于struts,hibernate,spring这三个框架无关.使用得当特别方便.基于注解的xml文件配置方式也受到人们的 ...

  9. Java自定义注解的实现

    Java自定义注解的实现,总共三步(eg.@RandomlyThrowsException): 1.首先编写一个自定义注解@RandomlyThrowsException package com.gi ...

  10. Java自定义注解开发

    一.背景 最近在自己搞一个项目时,遇到可需要开发自定义注解的需求,对于没有怎么关注这些java新特性的来说,比较尴尬,索性就拿出一些时间,来进行研究下自定义注解开发的步骤以及使用方式.今天在这里记下, ...

随机推荐

  1. python教程3.3:字符和编码

    1.二进制 计算机只能存储和识别二进制,但是人类常用的字母.数字.汉字怎么用计算机存储和识别呢? 人类强行约定一个对应表,把数字.字母和数字进行对应上,这样就可以用二进制表示字母和数字了. 2.ASC ...

  2. 用poi-tl导出word报告-支持表格文字刷色、背景刷色、表格合并单元格

    官方教程地址:http://deepoove.com/poi-tl/ apache poi 入门示例教程:http://deepoove.com/poi-tl/apache-poi-guide.htm ...

  3. 版本管理工具 nvm WIN版

    nvm -h //查看nvm的指令 nvm list //查看本地已经安装的node版本列表 nvm list available //查看可以安装的node版本 nvm install latest ...

  4. PageOffice6最简集成代码(Asp.Net)

    本文描述了PageOffice产品在普通的Asp.Net项目中如何集成调用. 新建Asp.Net项目:PageOffice6-Net-Simple 在您的web项目的"依赖项-包-管理NuG ...

  5. mybatis-plus id在高并发下出现重复

    mybaits-plus ASSIGN_ID生成 id生成策略 在分布式高并发环境下出现重复id https://github.com/baomidou/mybatis-plus/issues/307 ...

  6. CentOS7配置NFS服务并设置客户端自动挂载

    在CentOS7中配置NFS服务并设置客户端自动挂载的步骤如下: NFS服务端配置 安装NFS服务: 首先,你需要在CentOS 7服务器上安装NFS服务.你可以使用yum命令来安装: yum ins ...

  7. golang select 和外层的 for 搭配

    select语句通常与for循环搭配使用,但并不是必须的. 在某些情况下,select可能会直接放在一个独立的goroutine中,没有外层的for循环. 这通常发生在你知道只会有一次或有限次操作的情 ...

  8. Windows平台git clone文件路径太长报错

    问题描述 在Windows下拉取一些比较大的开源项目经常会提示文件路径太长(filename too long),然后死活都不成功 解决办法 1.配置git git config --system c ...

  9. Qt QMainWindow的使用

    参考视频:黑马科技:https://www.bilibili.com/video/BV1XW411x7NU?p=19 QMainWindow是一个为用户提供主窗口程序的类,包含一个菜单栏(menu b ...

  10. JavaSE 关键字和标识符

    目录 关键字 标识符 标识符命名规则 标识符命名规范 字面值 关键字 具有特殊含义的 命名时不可以与关键字重名 标识符 也就是名字,对类名,变量名称,方法名称,参数名称等修饰 标识符命名规则 以字母, ...