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

相关文章

Spring Boot 实现 IP 限流的原理、实践与利弊解析

《SpringBoot实现IP限流的原理、实践与利弊解析》在SpringBoot中实现IP限流是一种简单而有效的方式来保障系统的稳定性和可用性,本文给大家介绍SpringBoot实现IP限... 目录一、引言二、IP 限流原理2.1 令牌桶算法2.2 漏桶算法三、使用场景3.1 防止恶意攻击3.2 控制资源

Mac系统下卸载JAVA和JDK的步骤

《Mac系统下卸载JAVA和JDK的步骤》JDK是Java语言的软件开发工具包,它提供了开发和运行Java应用程序所需的工具、库和资源,:本文主要介绍Mac系统下卸载JAVA和JDK的相关资料,需... 目录1. 卸载系统自带的 Java 版本检查当前 Java 版本通过命令卸载系统 Java2. 卸载自定

springboot下载接口限速功能实现

《springboot下载接口限速功能实现》通过Redis统计并发数动态调整每个用户带宽,核心逻辑为每秒读取并发送限定数据量,防止单用户占用过多资源,确保整体下载均衡且高效,本文给大家介绍spring... 目录 一、整体目标 二、涉及的主要类/方法✅ 三、核心流程图解(简化) 四、关键代码详解1️⃣ 设置

Java Spring ApplicationEvent 代码示例解析

《JavaSpringApplicationEvent代码示例解析》本文解析了Spring事件机制,涵盖核心概念(发布-订阅/观察者模式)、代码实现(事件定义、发布、监听)及高级应用(异步处理、... 目录一、Spring 事件机制核心概念1. 事件驱动架构模型2. 核心组件二、代码示例解析1. 事件定义

SpringMVC高效获取JavaBean对象指南

《SpringMVC高效获取JavaBean对象指南》SpringMVC通过数据绑定自动将请求参数映射到JavaBean,支持表单、URL及JSON数据,需用@ModelAttribute、@Requ... 目录Spring MVC 获取 JavaBean 对象指南核心机制:数据绑定实现步骤1. 定义 Ja

Nginx 配置跨域的实现及常见问题解决

《Nginx配置跨域的实现及常见问题解决》本文主要介绍了Nginx配置跨域的实现及常见问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来... 目录1. 跨域1.1 同源策略1.2 跨域资源共享(CORS)2. Nginx 配置跨域的场景2.1

Python中提取文件名扩展名的多种方法实现

《Python中提取文件名扩展名的多种方法实现》在Python编程中,经常会遇到需要从文件名中提取扩展名的场景,Python提供了多种方法来实现这一功能,不同方法适用于不同的场景和需求,包括os.pa... 目录技术背景实现步骤方法一:使用os.path.splitext方法二:使用pathlib模块方法三

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器

CSS实现元素撑满剩余空间的五种方法

《CSS实现元素撑满剩余空间的五种方法》在日常开发中,我们经常需要让某个元素占据容器的剩余空间,本文将介绍5种不同的方法来实现这个需求,并分析各种方法的优缺点,感兴趣的朋友一起看看吧... css实现元素撑满剩余空间的5种方法 在日常开发中,我们经常需要让某个元素占据容器的剩余空间。这是一个常见的布局需求

HTML5 getUserMedia API网页录音实现指南示例小结

《HTML5getUserMediaAPI网页录音实现指南示例小结》本教程将指导你如何利用这一API,结合WebAudioAPI,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步... 目录1. html5 getUserMedia API简介1.1 API概念与历史1.2 功能与优势1.3