java自定义注解实现数据库字段判重

2023-12-23 18:12

本文主要是介绍java自定义注解实现数据库字段判重,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文使用mybatisplus版本:3.5.3.1

参考了两篇文章:

https://www.jianshu.com/p/6b6454073c89

https://www.cnblogs.com/xiaokangk/p/14208090.html

自己再实现的原因,mybatisplus版本升级了,包名也对应变化了。

增强实现的点:

1.增加子注解,实现多条件每个条件单独判充重

2.对于没有TableField的字段,用驼峰转下划线的字段

一、代码实现如下

1.注解定义

基本注解定义

package vip.xiaonuo.common.validator;import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;/***  <p> 自定义字段对应数据库内容重复校验 注解 </p>** @author dq* @description :* @author : zhengqing* @date : 2019/9/10 9:32*/
// 元注解: 给其他普通的标签进行解释说明 【@Retention、@Documented、@Target、@Inherited、@Repeatable】
@Documented
/*** 指明生命周期:*      RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。*/
@Retention(RetentionPolicy.RUNTIME)
/*** 指定注解运用的地方:*      ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举*/
@Target({ElementType.TYPE})
@Constraint(validatedBy = FieldRepeatValidatorClass.class)
public @interface FieldRepeatValidator {/*** 需要校验的字段* @return*/String[] fields() default {};/*** 排序* @return*/int order() default 0;/*** 默认错误提示信息* @return*/String message() default "字段内容重复!";Class<?>[] groups() default {};Class<? extends Payload>[]  payload() default {};}

子注解定义

package vip.xiaonuo.common.validator;import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface FieldRepeatValidators {FieldRepeatValidator[] value();}

2.校验

校验主类

package vip.xiaonuo.common.validator;import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;/***  <p> FieldRepeatValidator注解接口实现类 </p>** @description :*        技巧01:必须实现ConstraintValidator接口*     技巧02:实现了ConstraintValidator接口后即使不进行Bean配置,spring也会将这个类进行Bean管理*     技巧03:可以在实现了ConstraintValidator接口的类中依赖注入其它Bean*     技巧04:实现了ConstraintValidator接口后必须重写 initialize 和 isValid 这两个方法;*              initialize 方法主要来进行初始化,通常用来获取自定义注解的属性值;*              isValid 方法主要进行校验逻辑,返回true表示校验通过,返回false表示校验失败,通常根据注解属性值和实体类属性值进行校验判断 [Object:校验字段的属性值]* @author : zhengqing* @date : 2019/9/10 9:22*/
public class FieldRepeatValidatorClass implements ConstraintValidator<FieldRepeatValidator, Object> {private String[] field;private String message;@Overridepublic void initialize(FieldRepeatValidator fieldRepeatValidator) {this.field = fieldRepeatValidator.fields();this.message = fieldRepeatValidator.message();}@Overridepublic boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {return new FieldRepeatValidatorUtils().fieldRepeat(field, o, message);}}

校验工具类

package vip.xiaonuo.common.validator;import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;import java.lang.reflect.Field;
import java.util.*;/***  <p> 数据库字段内容重复判断处理工具类 </p>** @author:  zhengqing <br/>* @date:  2019/9/10$ 9:28$ <br/>* @version:  <br/>*/
@Slf4j
public class FieldRepeatValidatorUtils {/*** 实体类中id字段*/private String idColumnName;/*** 实体类中id的值*/private Object idColumnValue;/*** 校验数据 TODO 后期如果需要校验同个字段是否重复的话,将 `field` 做 , 或 - 分割... ;  如果id不唯一考虑传值过来判断 或 取fields第二个字段值拿id** @param fields:校验字段* @param o:对象数据* @param message:回调到前端提示消息* @return: boolean*/public boolean fieldRepeat(String[] fields, Object o, String message) {try {// 没有校验的值返回trueif(fields != null && fields.length == 0){return true;}checkUpdateOrSave(o);checkRepeat(fields,o,message);return true;}catch (Exception e){String msg = "验证字段是否重复报错";log.error(msg,e);throw new CustomerValidateException(e.getMessage());}}/*** 通过传入的实体类中 @TableId 注解的值是否为空,来判断是更新还是保存* 将值id值和id列名赋值* id的值不为空 是更新 否则是插入* @param o 被注解修饰过的实体类* @return*/public void checkUpdateOrSave(Object o) throws Exception{Field[] fields = getAllFields(o.getClass());for (Field f:fields) {// 设置私有属性可读f.setAccessible(true);if(f.isAnnotationPresent(TableId.class)){TableId tableId = f.getAnnotation(TableId.class);String value = tableId.value();if(StringUtils.isNotEmpty(value)){idColumnName = value;}else{//TableId 注解的value为空,则使用字段名驼峰转下划线idColumnName = StringUtils.camelToUnderline(f.getName());}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){Model model = (Model) o;QueryWrapper entityWrapper = new QueryWrapper();Map<String,Object> queryMap = getColumns(fields,o);Iterator<Map.Entry<String, Object>> it = queryMap.entrySet().iterator();while (it.hasNext()) {Map.Entry<String, Object> entry = it.next();entityWrapper.eq(entry.getKey(),entry.getValue());}if(idColumnValue != null){//更新的话,那条件就要排除自身entityWrapper.ne(idColumnName,idColumnValue);}List list = model.selectList(entityWrapper);if(list != null && list.size()>0){throw new CustomerValidateException(message);}}/*** 多条件判断唯一性,将我们的属性和值组装在map中,方便后续拼接条件* @param fields* @param o* @return*/public Map<String,Object> getColumns(String [] fields,Object o){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){try {if(f.isAnnotationPresent(TableField.class)){TableField tableField = f.getAnnotation(TableField.class);Object val = f.get(o);map.put(tableField.value(),val);}else  {//没有 TableField 注解时,试用驼峰转下划线 生成对应字段String tableFieldValue = StringUtils.camelToUnderline(f.getName());Object val = f.get(o);map.put(tableFieldValue,val);}}catch (IllegalAccessException i){throw new CustomerValidateException("获取字段的值报错");}}}

3.自定义异常

这里自定义一个异常类CustomerValidateException

package vip.xiaonuo.common.validator;public class CustomerValidateException extends RuntimeException{public CustomerValidateException(String message) {super(message);}
}

4.手动校验工具类

这里自定义手动校验工具类,用于非传参场景使用

package vip.xiaonuo.common.validator;import vip.xiaonuo.common.exception.CommonException;import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidationException;
import javax.validation.Validator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;/*** 校验工具类* 手动校验参数**/
public class ValidateUtil {/*** 校验器* @param t         参数* @param <T>       参数类型* @return*/public static <T> List<String> valid(T t){Validator validatorFactory = Validation.buildDefaultValidatorFactory().getValidator();Set<ConstraintViolation<T>> errors = new HashSet<>();try{errors = validatorFactory.validate(t);}catch (ValidationException e1 ){//这里e.getMessage() = HV000028: Unexpected exception during isValid call.throw new CommonException(e1.getCause().getMessage());}catch (CustomerValidateException e){throw new CommonException(e.getMessage());}return errors.stream().map(error -> error.getMessage()).collect(Collectors.toList());}
}

二、代码使用如下

注解加到类上。


1.单字段判断重复

使用@FieldRepeatValidator注解

@FieldRepeatValidator(fields = {"code"},message = "编号重复")


2.多字段判断重复

fields支持多个

@FieldRepeatValidator(fields = {"code","name"},message = "编号+名称重复")


3.子注解多组合判断重复

使用@FieldRepeatValidators注解嵌套@FieldRepeatValidator注解

@FieldRepeatValidators(value = {@FieldRepeatValidator(fields = {"code"},message = "编号重复"),@FieldRepeatValidator(fields = {"name"},message = "名称重复"),@FieldRepeatValidator(fields = {"code","name"},message = "编号+名称重复")
})


4.实体entity需要继承Model

工具类里强转的类型

 extends Model<T>

早期这里的版本是继承BaseEntity


5.手动校验工具类的使用

实体insert/update前加手动校验代码

ValidateUtil.valid(userEntity);

这篇关于java自定义注解实现数据库字段判重的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/528957

相关文章

Oracle查询优化之高效实现仅查询前10条记录的方法与实践

《Oracle查询优化之高效实现仅查询前10条记录的方法与实践》:本文主要介绍Oracle查询优化之高效实现仅查询前10条记录的相关资料,包括使用ROWNUM、ROW_NUMBER()函数、FET... 目录1. 使用 ROWNUM 查询2. 使用 ROW_NUMBER() 函数3. 使用 FETCH FI

Python脚本实现自动删除C盘临时文件夹

《Python脚本实现自动删除C盘临时文件夹》在日常使用电脑的过程中,临时文件夹往往会积累大量的无用数据,占用宝贵的磁盘空间,下面我们就来看看Python如何通过脚本实现自动删除C盘临时文件夹吧... 目录一、准备工作二、python脚本编写三、脚本解析四、运行脚本五、案例演示六、注意事项七、总结在日常使用

Java实现Excel与HTML互转

《Java实现Excel与HTML互转》Excel是一种电子表格格式,而HTM则是一种用于创建网页的标记语言,虽然两者在用途上存在差异,但有时我们需要将数据从一种格式转换为另一种格式,下面我们就来看看... Excel是一种电子表格格式,广泛用于数据处理和分析,而HTM则是一种用于创建网页的标记语言。虽然两

java图像识别工具类(ImageRecognitionUtils)使用实例详解

《java图像识别工具类(ImageRecognitionUtils)使用实例详解》:本文主要介绍如何在Java中使用OpenCV进行图像识别,包括图像加载、预处理、分类、人脸检测和特征提取等步骤... 目录前言1. 图像识别的背景与作用2. 设计目标3. 项目依赖4. 设计与实现 ImageRecogni

Java中Springboot集成Kafka实现消息发送和接收功能

《Java中Springboot集成Kafka实现消息发送和接收功能》Kafka是一个高吞吐量的分布式发布-订阅消息系统,主要用于处理大规模数据流,它由生产者、消费者、主题、分区和代理等组件构成,Ka... 目录一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者一、Kaf

Java访问修饰符public、private、protected及默认访问权限详解

《Java访问修饰符public、private、protected及默认访问权限详解》:本文主要介绍Java访问修饰符public、private、protected及默认访问权限的相关资料,每... 目录前言1. public 访问修饰符特点:示例:适用场景:2. private 访问修饰符特点:示例:

数据库oracle用户密码过期查询及解决方案

《数据库oracle用户密码过期查询及解决方案》:本文主要介绍如何处理ORACLE数据库用户密码过期和修改密码期限的问题,包括创建用户、赋予权限、修改密码、解锁用户和设置密码期限,文中通过代码介绍... 目录前言一、创建用户、赋予权限、修改密码、解锁用户和设置期限二、查询用户密码期限和过期后的修改1.查询用

详解Java如何向http/https接口发出请求

《详解Java如何向http/https接口发出请求》这篇文章主要为大家详细介绍了Java如何实现向http/https接口发出请求,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 用Java发送web请求所用到的包都在java.net下,在具体使用时可以用如下代码,你可以把它封装成一

mysql数据库分区的使用

《mysql数据库分区的使用》MySQL分区技术通过将大表分割成多个较小片段,提高查询性能、管理效率和数据存储效率,本文就来介绍一下mysql数据库分区的使用,感兴趣的可以了解一下... 目录【一】分区的基本概念【1】物理存储与逻辑分割【2】查询性能提升【3】数据管理与维护【4】扩展性与并行处理【二】分区的

使用Python实现在Word中添加或删除超链接

《使用Python实现在Word中添加或删除超链接》在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能,本文将为大家介绍一下Python如何实现在Word中添加或... 在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能。通过添加超