公共字段抽取自动填充

目录

一、问题分析

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

}
相关推荐
不爱说话郭德纲2 小时前
告别漫长的HbuilderX云打包排队!uni-app x 安卓本地打包保姆级教程(附白屏、包体积过大排坑指南)
android·前端·uni-app
程序员清风5 小时前
程序员兼职必看:靠谱软件外包平台挑选指南与避坑清单!
java·后端·面试
李广坤5 小时前
MySQL 大表字段变更实践(改名 + 改类型 + 改长度)
数据库
皮皮林5516 小时前
利用闲置 Mac 从零部署 OpenClaw 教程 !
java
Sinclair6 小时前
简单几步,安卓手机秒变服务器,安装 CMS 程序
android·服务器
雮尘9 小时前
手把手带你玩转Android gRPC:一篇搞定原理、配置与客户端开发
android·前端·grpc
ktl11 小时前
Android 编译加速/优化 80%:一个文件搞定,零侵入零配置
android
华仔啊11 小时前
挖到了 1 个 Java 小特性:var,用完就回不去了
java·后端
SimonKing11 小时前
SpringBoot整合秘笈:让Mybatis用上Calcite,实现统一SQL查询
java·后端·程序员
alexhilton1 天前
使用FunctionGemma进行设备端函数调用
android·kotlin·android jetpack