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

相关文章

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

C#实现将Excel表格转换为图片(JPG/ PNG)

《C#实现将Excel表格转换为图片(JPG/PNG)》Excel表格可能会因为不同设备或字体缺失等问题,导致格式错乱或数据显示异常,转换为图片后,能确保数据的排版等保持一致,下面我们看看如何使用C... 目录通过C# 转换Excel工作表到图片通过C# 转换指定单元格区域到图片知识扩展C# 将 Excel

Java使用ANTLR4对Lua脚本语法校验详解

《Java使用ANTLR4对Lua脚本语法校验详解》ANTLR是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件,下面就跟随小编一起看看Java如何使用ANTLR4对Lua脚本... 目录什么是ANTLR?第一个例子ANTLR4 的工作流程Lua脚本语法校验准备一个Lua Gramm

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

Java Optional的使用技巧与最佳实践

《JavaOptional的使用技巧与最佳实践》在Java中,Optional是用于优雅处理null的容器类,其核心目标是显式提醒开发者处理空值场景,避免NullPointerExce... 目录一、Optional 的核心用途二、使用技巧与最佳实践三、常见误区与反模式四、替代方案与扩展五、总结在 Java

基于Java实现回调监听工具类

《基于Java实现回调监听工具类》这篇文章主要为大家详细介绍了如何基于Java实现一个回调监听工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录监听接口类 Listenable实际用法打印结果首先,会用到 函数式接口 Consumer, 通过这个可以解耦回调方法,下面先写一个

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

Qt中QGroupBox控件的实现

《Qt中QGroupBox控件的实现》QGroupBox是Qt框架中一个非常有用的控件,它主要用于组织和管理一组相关的控件,本文主要介绍了Qt中QGroupBox控件的实现,具有一定的参考价值,感兴趣... 目录引言一、基本属性二、常用方法2.1 构造函数 2.2 设置标题2.3 设置复选框模式2.4 是否

Java字符串处理全解析(String、StringBuilder与StringBuffer)

《Java字符串处理全解析(String、StringBuilder与StringBuffer)》:本文主要介绍Java字符串处理全解析(String、StringBuilder与StringBu... 目录Java字符串处理全解析:String、StringBuilder与StringBuffer一、St

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指