公共字段自动填充

在做项目的时候,很多数据库表会有相同字段,例如很多表都会记下数据的创造时间、修改时间、创造者、修改者,便于后期维护。如果每次对数据的增加和修改操作都需要手动添加后修改这些字段,则会有大量的相同代码,也影响了代码的可读性。为此,我们可以利用反射机制和AOP(面向切面编程),对这些公共字段进行统一处理


序号 含义 数据类型 操作
1 创建时间 datetime insert
2 创建人id bigint insert
3 修改时间 datetime insert、update
4 修改人id bigint insert、update

解决方案

  • 自定义注解AutoFill,用于标识需要进行公共字段自动填充的方法
  • 自定义切面类AutoFillAspect,统一拦截加入了AutoFill注解的方法,通过反射为公共字段赋值
  • 在Mapper的方法上加入AutoFill注解

技术点: 枚举、注解、AOP、反射


自定义注解AutoFill

1
2
3
4
5
6
7
8
9
/**
* @description: 自定义注解,用于识别某个方法需要进行功能字段自动填充
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
// 指定数据库操作的类型 update insert
OperationType value();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 数据库操作类型
*/
public enum OperationType {
/**
* 更新操作
*/
UPDATE,

/**
* 插入操作
*/
INSERT
}

OperationType 是自定义的枚举类

@Target 和 @Retention,可以用来修饰注解,是注解的注解,被称为元注解

@Target : 目标,即该注解可以声明在哪些目标元素之前,也可理解为注释类型的程序元素的种类

  • ElementType.PACKAGE:该注解只能声明在一个包名前
  • ElementType.ANNOTATION_TYPE:该注解只能声明在一个注解类型前
  • ElementType.TYPE:该注解只能声明在一个类前
  • ElementType.CONSTRUCTOR:该注解只能声明在一个类的构造方法前
  • ElementType.LOCAL_VARIABLE:该注解只能声明在一个局部变量前
  • ElementType.METHOD:该注解只能声明在一个类的方法前
  • ElementType.PARAMETER:该注解只能声明在一个方法参数前
  • ElementType.FIELD:该注解只能声明在一个类的字段前

**@Retention :保留,可以理解为如何保留,即告诉编译程序如何处理,也可理解为注解类的生命周期 **

  • RetentionPolicy.SOURCE : 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃
  • RetentionPolicy.CLASS : 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期
  • RetentionPolicy.RUNTIME : 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在

自定义切面类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
**
* @description: 自定义切面
*/
@Aspect
@Component
@Slf4j
public class AutoFillAspect {

/**
* @description: 切入点
*/
@Pointcut("@annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut(){}


/**
* @description: 前置通知
*/
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint) {
log.info("开始进行公共字段自动填充---");

// 获取到当前被拦截的方法上的数据库操作类型
MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 方法签名对象
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); // 获得方法上的注解对象
OperationType operationType = autoFill.value(); // 获得数据库操作类型

// 获取到当前被拦截的方法的参数--实体对象
Object[] args = joinPoint.getArgs();
if (args == null || args.length == 0) {
return;
}
Object entity = args[0]; // 在写新增和修改方法时,把实体放在第一个参数

// 准备赋值的数据
LocalDateTime now = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();

// 根据当前不同的操作类型,为对应的属性通过反射来赋值
if (operationType == OperationType.INSERT) {
// 为4个字段赋值
try {
Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);

// 通过反射为对象属性赋值
setCreateTime.invoke(entity,now);
setCreateUser.invoke(entity,currentId);
setUpdateTime.invoke(entity,now);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
e.printStackTrace();
}
} else if (operationType == OperationType.UPDATE) {
// 为2个字段赋值
try {
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

// 通过反射为对象属性赋值
setUpdateTime.invoke(entity,now);
setUpdateUser.invoke(entity,currentId);
} catch (Exception e) {
e.printStackTrace();
}
}

}

}

在执行update和save方法前就需要对公共字段赋值,所以这里使用的是前置通知
@Pointcut(“@annotation(com.sky.annotation.AutoFill)”),com.sky.annotation.AutoFill 是自定义注解AutoFill类的位置
update方法只需要为2个公共字段赋值,save方法需要为4个公共字段赋值


在Mapper的方法上加入AutoFill注解

1
2
3
4
5
6
7
8
9
@AutoFill(value = OperationType.INSERT)
@Insert("insert into employee (name, username, password, phone, sex, id_number, status, create_time, update_time, create_user, update_user) " +
"values " +
"(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{status},#{createTime},#{updateTime},#{createUser},#{updateUser})")
void insert(Employee employee);


@AutoFill(value = OperationType.UPDATE)
void update(Employee employee);

公共字段自动填充
https://lzhengjy.github.io/2023/10/21/公共字段自动填充/
作者
Zheng
发布于
2023年10月21日
许可协议