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

相关文章

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

破茧 JDBC:MyBatis 在 Spring Boot 中的轻量实践指南

《破茧JDBC:MyBatis在SpringBoot中的轻量实践指南》MyBatis是持久层框架,简化JDBC开发,通过接口+XML/注解实现数据访问,动态代理生成实现类,支持增删改查及参数... 目录一、什么是 MyBATis二、 MyBatis 入门2.1、创建项目2.2、配置数据库连接字符串2.3、入

Springboot项目启动失败提示找不到dao类的解决

《Springboot项目启动失败提示找不到dao类的解决》SpringBoot启动失败,因ProductServiceImpl未正确注入ProductDao,原因:Dao未注册为Bean,解决:在启... 目录错误描述原因解决方法总结***************************APPLICA编

深度解析Spring Security 中的 SecurityFilterChain核心功能

《深度解析SpringSecurity中的SecurityFilterChain核心功能》SecurityFilterChain通过组件化配置、类型安全路径匹配、多链协同三大特性,重构了Spri... 目录Spring Security 中的SecurityFilterChain深度解析一、Security

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

SpringBoot多环境配置数据读取方式

《SpringBoot多环境配置数据读取方式》SpringBoot通过环境隔离机制,支持properties/yaml/yml多格式配置,结合@Value、Environment和@Configura... 目录一、多环境配置的核心思路二、3种配置文件格式详解2.1 properties格式(传统格式)1.