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

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu