公共字段抽取自动填充

目录

一、问题分析

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

}
相关推荐
一瓢西湖水10 小时前
列式数据库-以clickHouse为例
数据库·clickhouse
Elastic 中国社区官方博客10 小时前
使用 Elastic Cloud Serverless 扩展批量索引
大数据·运维·数据库·elasticsearch·搜索引擎·云原生·serverless
liulanba10 小时前
AI Agent技术完整指南 第一部分:基础理论
数据库·人工智能·oracle
没有bug.的程序员11 小时前
服务安全:内部服务如何防止“裸奔”?
java·网络安全·云原生安全·服务安全·零信任架构·微服务安全·内部鉴权
逆天小北鼻11 小时前
Oracle 服务端与客户端的核心区分要点
数据库·oracle
2501_9462429311 小时前
MPV-EASY Player (MPV播放器) v0.41.0.1
数据库·经验分享·云计算·计算机外设·github·电脑·csdn开发云
一线大码11 小时前
SpringBoot 3 和 4 的版本新特性和升级要点
java·spring boot·后端
weixin_4407305011 小时前
java数组整理笔记
java·开发语言·笔记
weixin_4250230011 小时前
Spring Boot 实用核心技巧汇总:日期格式化、线程管控、MCP服务、AOP进阶等
java·spring boot·后端
踏雪羽翼11 小时前
android TextView实现文字字符不同方向显示
android·自定义view·textview方向·文字方向·textview文字显示方向·文字旋转·textview文字旋转