在数据库表的设计中主键id,创建时间create_time,更新时间update_time都是必须的字段,根据实际的需求可能还需要用到创建用户名create_username,和更新用户名update_username这两个字段。
其中的create_time、update_time、create_username、update_username都是属于是表的公共字段,如果每次在进行插入或者更新操作时都要手动赋值是很繁琐的。
通过AOP实现公共字段自动填充,只需给mapper层中的对应方法加上注解即可。
依赖
<!--AOP起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
OperateType枚举
枚举数据库操作类型
/**
* @Description: 数据库操作类型
* @Author: 翰戈.summer
* @Date: 2023/11/17
* @Param:
* @Return:
*/
public enum OperateType {
//插入操作
INSERT,
//更新操作
UPDATE
}
AutoFill注解
AutoFill注解,用于标识需要进行公共字段自动填充的方法。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Description: 注解,用于标识需要进行公共字段自动填充的方法
* @Author: 翰戈.summer
* @Date: 2023/11/18
* @Param:
* @Return:
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
/**
* 数据库操作类型,INSERT插入操作,UPDATE更新操作
*/
OperateType value();
}
AutoFillAspect切面类
切入点一定要写对,这里用到的是前置通知,拦截mapper层接口的方法,获取方法参数进行公共字段填充。
import org.aspectj.lang.JoinPoint;
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.Method;
import java.time.LocalDateTime;
/**
* @Description: 切面类,实现公共字段自动填充
* @Author: 翰戈.summer
* @Date: 2023/11/19
* @Param:
* @Return:
*/
@Aspect
@Component
public class AutoFillAspect {
/**
* @Description: 切入点
* @Author: 翰戈.summer
* @Date: 2023/11/19
* @Param:
* @Return: void
*/
@Pointcut("execution(* com.demo.mapper.*.*(..)) && @annotation(com.demo.annotation.AutoFill)")
public void autoFillPointcut() {
}
/**
* @Description: 前置通知,进行公共字段自动填充
* @Author: 翰戈.summer
* @Date: 2023/11/19
* @Param: JoinPoint
* @Return: void
*/
@Before("autoFillPointcut()")
public void autoFill(JoinPoint joinPoint) {
//获取方法签名对象
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//获取注解对象
AutoFill autoFill = methodSignature.getMethod().getAnnotation(AutoFill.class);
//获取数据库操作类型
OperateType operateType = autoFill.value();
//获取拦截方法实体类对象参数
Object[] args = joinPoint.getArgs();
if (args == null || args.length == 0) {
return;
}
Object entity = args[0];
//获取填充数据
String username = BaseContext.getContext();// 当前用户名
LocalDateTime now = LocalDateTime.now();// 当前时间
//为插入操作填充数据
if (operateType == OperateType.INSERT) {
try {
Method setCreateUsername = entity.getClass()
.getDeclaredMethod("setCreateUsername", String.class);
Method setUpdateUsername = entity.getClass()
.getDeclaredMethod("setUpdateUsername", String.class);
Method setCreateTime = entity.getClass()
.getDeclaredMethod("setCreateTime", LocalDateTime.class);
Method setUpdateTime = entity.getClass()
.getDeclaredMethod("setUpdateTime", LocalDateTime.class);
setCreateUsername.invoke(entity, username);
setUpdateUsername.invoke(entity, username);
setCreateTime.invoke(entity, now);
setUpdateTime.invoke(entity, now);
} catch (Exception ex) {
throw new RuntimeException();
}
}
//为更新操作填充数据
if (operateType == OperateType.UPDATE) {
try {
Method setUpdateUsername = entity.getClass()
.getDeclaredMethod("setUpdateUsername", String.class);
Method setUpdateTime = entity.getClass()
.getDeclaredMethod("setUpdateTime", LocalDateTime.class);
setUpdateUsername.invoke(entity, username);
setUpdateTime.invoke(entity, now);
} catch (Exception ex) {
throw new RuntimeException();
}
}
}
}
BaseContext上下文
线程局部变量,用于存放用户名,便于在AutoFillAspect切面类中获取。
/**
* @Description: 线程局部变量
* @Author: 翰戈.summer
* @Date: 2023/11/17
* @Param:
* @Return:
*/
public class BaseContext {
public static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void setContext(String context) {
threadLocal.set(context);
}
public static String getContext() {
return threadLocal.get();
}
public static void removeContext() {
threadLocal.remove();
}
}