公共字段抽取自动填充

目录

一、问题分析

当执行sql语句(增删改查)的时候,需要同时更新修改人以及修改时间,如果在每一次执行sql的时候在代码中添加该字段的话,代码会非常的冗余,不好管理,因此使用公共字段自动填充可以更好的实现该功能。

二、实现思路

通过切面进行统一处理,分为两种情况(新增和修改),查询和删除不用修改,新增需要更新四个字段,修改需要更新两个字段。

三、自定义注解实现

创建注解实现类:


自定义中注解代码如下:

java 复制代码
package com.sky.annonation;
import com.sky.enumeration.OperationType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
    OperationType value();
}

**@Target(ElementType.METHOD)**是说这个注解使用到方法上。

**@Retention(RetentionPolicy.RUNTIME)**是说这个注解在运行的时候起作用
OperationType value(); 是枚举类。

枚举类中代码如下:

java 复制代码
package com.sky.enumeration;

/**
 * 数据库操作类型
 */
public enum OperationType {

    /**
     * 更新操作
     */
    UPDATE,

    /**
     * 插入操作
     */
    INSERT

}

四、自定义切面实现类

java 复制代码
package com.sky.aspect;

import com.sky.annonation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

@Component
@Aspect
@Slf4j
public class AutoFillAspect {
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annonation.AutoFill)")
    public void AutoFill(){
        log.info("开始进行数据填充");
    }
    @Before("AutoFill()")
    public void before(JoinPoint joinPoint) throws NoSuchMethodException {
        log.info("开始进行数据填充");

//        只有当修改和新增方法才需要拦截,获得数据库操作类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
        OperationType value = autoFill.value();
//        获得被拦截方法的参数
        Object[] args = joinPoint.getArgs();
        if(args == null || args.length == 0){
            return;
        }
        Object object = args[0];
        if(value == OperationType.INSERT){
            LocalDateTime now = LocalDateTime.now();
            Long id = BaseContext.getCurrentId();
            Method declaredMethod = object.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME , LocalDateTime.class);
            Method declaredMethod1 = object.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
            Method declaredMethod2 = object.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
            Method declaredMethod3 = object.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
            try {
                declaredMethod.invoke(object,now);
                declaredMethod1.invoke(object,id);
                declaredMethod2.invoke(object,now);
                declaredMethod3.invoke(object,id);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }else if(value == OperationType.UPDATE){
            LocalDateTime now = LocalDateTime.now();
            Long id = BaseContext.getCurrentId();
            Method declaredMethod = object.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME , LocalDateTime.class);
            Method declaredMethod1 = object.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
            try {
                declaredMethod.invoke(object,now);
                declaredMethod1.invoke(object,id);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
}

注明这个类是切面类,加入@aspect注解,@compent注解将这个类交给容器管理。

定义切入点:

java 复制代码
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annonation.AutoFill)")
    public void AutoFill(){
        log.info("开始进行数据填充");
    }

当mapper层级下的各个方法执行的时候就会执行这个切面类,或者加入@AutoFill注解的时候也会执行这个切面类。

java 复制代码
    @Before("AutoFill()")
    public void before(JoinPoint joinPoint) throws NoSuchMethodException {
        log.info("开始进行数据填充");

//        只有当修改和新增方法才需要拦截,获得数据库操作类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
        OperationType value = autoFill.value();
//        获得被拦截方法的参数
        Object[] args = joinPoint.getArgs();
        if(args == null || args.length == 0){
            return;
        }
        Object object = args[0];
        if(value == OperationType.INSERT){
            LocalDateTime now = LocalDateTime.now();
            Long id = BaseContext.getCurrentId();
            Method declaredMethod = object.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME , LocalDateTime.class);
            Method declaredMethod1 = object.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
            Method declaredMethod2 = object.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
            Method declaredMethod3 = object.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
            try {
                declaredMethod.invoke(object,now);
                declaredMethod1.invoke(object,id);
                declaredMethod2.invoke(object,now);
                declaredMethod3.invoke(object,id);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }else if(value == OperationType.UPDATE){
            LocalDateTime now = LocalDateTime.now();
            Long id = BaseContext.getCurrentId();
            Method declaredMethod = object.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME , LocalDateTime.class);
            Method declaredMethod1 = object.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
            try {
                declaredMethod.invoke(object,now);
                declaredMethod1.invoke(object,id);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

代码解释:

1、获取注解中的参数值,用来判断是新增还是修改。

2、获取mapper层中方法的参数值,获得到传入的实体类。

3、根据新增还是修改,利用反射为实体类中的修改人、修改时间、新增人、新增时间赋值。

五、代码测试

执行代码之后,利用调试工具我们可以看到

会标注好切入点,然后以及切入的方法是UPDATE还是INSERT。

获取实体类中的参数结果展示。

sql的执行结果如下:

六、其他相关类

java 复制代码
package com.sky.context;

public class BaseContext {

    public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    public static void setCurrentId(Long id) {
        threadLocal.set(id);
    }

    public static Long getCurrentId() {
        return threadLocal.get();
    }

    public static void removeCurrentId() {
        threadLocal.remove();
    }

}
相关推荐
java_logo4 分钟前
Apache Doris Docker 部署指南:实时分析数据库实战
数据库·docker·apache·doris·apache doris·轩辕镜像·docker部署doris
咩咩啃树皮6 小时前
第40篇:Vue3组件化开发精讲——组件拆分、复用、父子通信、工程化架构
java·前端·架构
鱟鲥鳚7 小时前
Spring Boot 集成 LangChain4j:从模型调用到 Tool Calling(Demo版)
java·spring boot
广州灵眸科技有限公司7 小时前
xfce桌面触摸校准:基于灵眸科技EASY-EAl-Orin-Nano
数据库·windows·科技
大模型码小白8 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
腾渊信息科技公司9 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
爱笑的源码基地10 小时前
高并发 Redis 缓存门诊HIS系统源码,含财务统计药房进销存
java·程序·门诊系统·诊所系统·云诊所源码
憧憬成为web高手10 小时前
皮卡丘靶场速通--sql 2
数据库·sql·mybatis
wuqingshun31415910 小时前
TCP超时重传机制是为了解决什么问题?
java
HLC++10 小时前
Linux的进程间通信
android·linux·服务器