公共字段抽取自动填充

目录

一、问题分析

当执行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();
    }

}
相关推荐
IvorySQL8 小时前
PostgreSQL 日报|修复截断 zstd 备份检测(10 月 9 日)
数据库·postgresql
aqi008 小时前
鸿蒙版本的电子书阅读APP开放源码啦
android·华为·ai编程·harmonyos·鸿蒙
墨天梦8 小时前
B19_SunnyWeather到阅读客户端
android·kotlin
刘胡子大叔8 小时前
SQL 脚本的导入顺序
数据库·sql
代码什么用8 小时前
Spring基础使用
java·后端·spring
代码什么用8 小时前
Spring对IoC的实现
数据库·spring
Su米苏9 小时前
Spring AI 中MCP 与普通 @Tool的区别
java·人工智能·spring
美狐美颜sdk9 小时前
直播APP源码可以直接接入视频美颜sdk吗?技术方案详解
android·人工智能·音视频·美颜sdk·直播美颜sdk
码云数智-园园9 小时前
unique_ptr 还是 shared_ptr?C++ 智能指针选型与内存泄漏实战分析
java·开发语言
老板一杯拿铁9 小时前
Java 异常体系详解:从字节码原理到最佳实践
java