MyBatis 字段自动填充(AutoFill)
场景
在数据库的添加或修改操作时,需要统一维护以下字段:
create_time- 创建时间create_user- 创建人update_time- 更新时间update_user- 更新人
手动在每个方法中设置容易遗漏,使用 AOP 切面实现自动填充。
核心思路
- 创建枚举类定义操作类型(INSERT / UPDATE)
- 创建注解
@AutoFill标记需要拦截的方法 - 创建切面类
AutoFillAspect,在方法执行前自动填充字段 - 在 Mapper 方法上添加
@AutoFill注解
一、定义枚举类
package com.sky.annotation;
/**
* 数据库操作类型
*/
public enum OperationType {
INSERT, // 插入操作
UPDATE // 更新操作
}
二、定义注解
package com.sky.annotation;
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(); // 指定操作类型
}
三、定义常量类
package com.sky.constant;
/**
* 自动填充字段名常量
*/
public class AutoFillConstant {
public static final String SET_CREATE_TIME = "setCreateTime";
public static final String SET_CREATE_USER = "setCreateUser";
public static final String SET_UPDATE_TIME = "setUpdateTime";
public static final String SET_UPDATE_USER = "setUpdateUser";
}
四、切面类实现
package com.sky.aspect;
import com.sky.annotation.AutoFill;
import com.sky.annotation.OperationType;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
/**
* 自动填充切面类
*/
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
/**
* 切入点:拦截 mapper 包下所有方法,且方法上有 @AutoFill 注解
*/
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut() {}
/**
* 前置通知:在方法执行前填充字段
*/
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint) {
log.info("开始自动填充字段...");
// 1. 获取操作类型
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
AutoFill autoFill = method.getAnnotation(AutoFill.class);
OperationType operationType = autoFill.value();
// 2. 获取方法参数(实体对象)
Object[] args = joinPoint.getArgs();
if (args == null || args.length == 0) return;
Object entity = args[0]; // 第一个参数是实体对象
// 3. 准备填充的数据
LocalDateTime now = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId(); // 获取当前登录用户ID
// 4. 根据操作类型填充字段
if (operationType == OperationType.INSERT) {
// INSERT:填充 4 个字段
try {
Method setCreateTime = entity.getClass().getMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setCreateUser = entity.getClass().getMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateTime = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
setCreateTime.invoke(entity, now);
setCreateUser.invoke(entity, currentId);
setUpdateTime.invoke(entity, now);
setUpdateUser.invoke(entity, currentId);
} catch (Exception e) {
log.error("自动填充失败", e);
}
} else if (operationType == OperationType.UPDATE) {
// UPDATE:填充 2 个字段
try {
Method setUpdateTime = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
setUpdateTime.invoke(entity, now);
setUpdateUser.invoke(entity, currentId);
} catch (Exception e) {
log.error("自动填充失败", e);
}
}
log.info("字段填充完成");
}
}
五、在 Mapper 方法上使用
@Mapper
public interface EmployeeMapper {
@AutoFill(OperationType.INSERT)
void insert(Employee employee);
@AutoFill(OperationType.UPDATE)
void update(Employee employee);
}
六、实体类示例
@Data
public class Employee {
private Long id;
private String name;
private String phone;
// 自动填充的字段
private LocalDateTime createTime;
private Long createUser;
private LocalDateTime updateTime;
private Long updateUser;
}
工作流程图
┌─────────────┐ ┌──────────────┐ ┌────────────────┐
│ 调用方法 │ ──► │ @AutoFill │ ──► │ AutoFillAspect│
│ insert(emp) │ │ 标记方法 │ │ 切面拦截 │
└─────────────┘ └──────────────┘ └───────┬────────┘
│
▼
┌────────────────┐
│ 反射调用 setter│
│ 填充字段值 │
└────────────────┘