在数据库表的设计中主键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();}
}