公共字段抽取自动填充

目录

一、问题分析

当执行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 小时前
校园快递|基于Java校园快递管理系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot
AndreasEmil3 小时前
JavaSE - 继承
java·开发语言·ide·vscode·intellij-idea·idea
后端小张3 小时前
【JAVA 进阶】SpringBoot自动配置机制:从原理到实践的深度解析
java·spring boot·后端·spring·spring cloud·自动配置·注解
马克学长9 小时前
SSM桐梓娄山书店管理系统6k3s6(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·用户管理·ssm框架·书店管理系统
毕设源码-赖学姐9 小时前
【开题答辩全过程】以 高校评教评学系统的设计与实现为例,包含答辩的问题和答案
java·eclipse
老华带你飞9 小时前
博物馆展览门户|基于Java博物馆展览门户系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·后端
路边草随风9 小时前
iceberg 基于 cosn 构建 catalog
java·大数据
It's now9 小时前
Spring Framework 7.0 原生弹性功能系统讲解
java·后端·spring
点PY9 小时前
C++ 中 std::async 和 std::future 的并发性
java·开发语言·c++