Java 自定义注解校验字段唯一性
业务场景
在项目中,某些情景下我们需要验证编码是否重复,账号是否重复,身份证号是否重复等...
那么有没有办法可以解决这类似的重复代码量呢?
我们可以通过自定义注解校验的方式去实现,在实体类上面加上自定义的注解即可
@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 自定义注解校验字段唯一性的更多相关文章
- Java自定义注解源码+原理解释(使用Java自定义注解校验bean传入参数合法性)
		Java自定义注解源码+原理解释(使用Java自定义注解校验bean传入参数合法性) 前言:由于前段时间忙于写接口,在接口中需要做很多的参数校验,本着简洁.高效的原则,便写了这个小工具供自己使用(内容 ... 
- Java 自定义注解 校验指定字段对应数据库内容重复
		一.前言 在项目中,某些情景下我们需要验证编码是否重复,账号是否重复,身份证号是否重复等... 而像验证这类代码如下: 那么有没有办法可以解决这类似的重复代码量呢? 我们可以通过自定义注解校验的方式去 ... 
- java自定义注解实现前后台参数校验
		2016.07.26 qq:992591601,欢迎交流 首先介绍些基本概念: Annotations(also known as metadata)provide a formalized way ... 
- java自定义注解类
		一.前言 今天阅读帆哥代码的时候,看到了之前没有见过的新东西, 比如java自定义注解类,如何获取注解,如何反射内部类,this$0是什么意思? 于是乎,学习并整理了一下. 二.代码示例 import ... 
- java自定义注解注解方法、类、属性等等【转】
		http://anole1982.iteye.com/blog/1450421 http://www.open-open.com/doc/view/51fe76de67214563b20b385320 ... 
- JAVA自定义注解 ------ Annotation
		日常开发工作中,合理的使用注解,可以简化代码编写以及使代码结构更加简单,下面记录下,JAVA自定义注解的开发过程. 定义注解声明类. 编写注解处理器(主要起作用部分). 使用注解. 相关知识点介绍, ... 
- Java自定义注解和运行时靠反射获取注解
		转载:http://blog.csdn.net/bao19901210/article/details/17201173/ java自定义注解 Java注解是附加在代码中的一些元信息,用于一些工具在编 ... 
- java自定义注解知识实例及SSH框架下,拦截器中无法获得java注解属性值的问题
		一.java自定义注解相关知识 注解这东西是java语言本身就带有的功能特点,于struts,hibernate,spring这三个框架无关.使用得当特别方便.基于注解的xml文件配置方式也受到人们的 ... 
- Java自定义注解的实现
		Java自定义注解的实现,总共三步(eg.@RandomlyThrowsException): 1.首先编写一个自定义注解@RandomlyThrowsException package com.gi ... 
- Java自定义注解开发
		一.背景 最近在自己搞一个项目时,遇到可需要开发自定义注解的需求,对于没有怎么关注这些java新特性的来说,比较尴尬,索性就拿出一些时间,来进行研究下自定义注解开发的步骤以及使用方式.今天在这里记下, ... 
随机推荐
- 小程序 image 高度自适应及裁剪问题
			在做微信小程序的商品详情页,商品的详情是图片集合,渲染完成后发现图片加载的很不自然,后来我把样式设置宽度 100%,并对 image 组件添加属性 mode="widthFix"解 ... 
- Ubuntu 启用交换分区
			前言 交换分区也称之为 swap 分区,允许系统在内存不足的情况下将内存程序写入文件,防止系统卡死失去响应的情况发生. 检查现有交换分区 首先,确认系统中是否已存在交换分区或文件.在终端中输入以下命令 ... 
- Python基础篇(流程控制)
			流程控制是程序运行的基础,流程控制决定了程序按照什么样的方式执行. 条件语句 条件语句一般用来判断给定的条件是否成立,根据结果来执行不同的代码,也就是说,有了条件语句,才可以根据不同的情况做不同的事, ... 
- 训练营 |【AIRIOT大学计划暑期训练营】第三期即将开营,报名从速!
			培养新生力量,聚焦产业融合.为了促进物联网产业的纵深发展和创新,推进教育链.产业链与创新链的有机衔接,提高学生理论.实践和创新能力,为行业培养更多优秀人才,航天科技控股集团股份有限公司将于2023年7 ... 
- pod(四):pod的重启策略和生命周期
			目录 一.系统环境 二.前言 三.pod的重启策略 四.pod的生命周期 一.系统环境 服务器版本 docker软件版本 Kubernetes(k8s)集群版本 CPU架构 CentOS Linux ... 
- unity 新input system 鼠标点在ui上检测的两种方法
			哪种有用就用哪种.EventSystem.current.IsPointerOverGameObject()有可能不好使. using System.Collections.Generic; usin ... 
- 前端如何对cookie加密
			在前端对 Cookie 进行加密时,你可以使用加密算法对 Cookie 的值进行加密,然后再将加密后的值存储到 Cookie 中.常用的加密算法包括对称加密算法(如 AES)和非对称加密算法(如 RS ... 
- C# dynamic动态对象赋值
			dynamic 如果接收的是匿名对象,是无法为属性赋值的,而如果是接收的定义对象,又无法扩展字段. 解决办法序列化为json字符串,然后用Dictionary反序列化,就能赋值了.也能扩展新的字段. ... 
- Tkinter界面实操
			常用opencv-python进行图像处理,有时需要图形用户界面,写个Demo以备不时之需. Tkinter 1. 导入库 由于 Tkinter 是内置到 python 的安装包中.只要安装好 Pyt ... 
- C#笔记 picturebox功能实现(滚动放大,拖动)
			代码链接 1. picturebox上的坐标与原图中坐标的转换 (1) 由于图片的长宽比例和picturebox的长宽比例不同,所以图片不想拉伸的话,左右或者上下会有留白.将picturebox的si ... 
