在插入或更新某张表的数据时往往需要记录更改的操作者(ID)和时间,而在多张表中都需要这种操作时,重复的编写同一段代码容易让我们不开心,那么有没有办法可以实现在插入或更新时自动的为我们填充如表中的字段呢?
答案是有的:利用自定义注解在我们需要的地方标识。
技术点:AOP / 注解 / 反射
字段名 | 操作类型 |
create_time | insert |
create_user | insert |
update_time | insert/update |
update_user | insert/update |
具体的思路是:
- 自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法
- 自定义切面类 AutoFillAspect,同意拦截加入 AutoFill 注解的方法,通过反射为公共字段赋值
- 在 Mapper 的方法上加入 AutoFill 注解
创建自定义注解
/** * 自定义注解 * @Target 定义该注解只能用在方法上 * @Retention 用于指定注解的生命周期 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AutoFill { // 定义注解属性,到时候可以通过 value 告诉切点是什么操作类型 // 这里的 Operation 类是自己创建的枚举 Operation value(); }
先创建
@AutoFill
注解,注意到内部有一个属性,待会使用注解时可以往里面传值。创建自定义切面类
@Aspect @Component public class AutoFillAspect { // 切入点 @PointCut("execution(* com.cuctut.mapper.*.*(..)) && @annotation(com.cuctut.annotation.AutoFill)") public void autoFillPointCut(){} // 切入点的前置通知 @Before("autoFillPointCut()") public void autoFill(JoinPoint joinPoint){ // TODO } }
在切面类里定义切入点,切入点规定在哪个包拦截哪个方法,并且规定要有我们自定义的注解。
拦截到方法后执行前置通知方法。
这里的 TODO 就是我们要实现效果的地方。
@Before("autoFillPointCut()") public void autoFill(JoinPoint joinPoint){ // 获取当前拦截方法上的数据库操作类型 INSERT / UPDATE 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) { try { Method setCreateTime = entity.getClass().getDeclaredMethod("setCreateTime",LocalDateTime.class); Method setCreateUser = entity.getClass().getDeclaredMethod("setCreateUser",Long.class); Method setUpdateTime = entity.getClass().getDeclaredMethod("setUpdateTime",LocalDateTime.class); Method setUpdateUser = entity.getClass().getDeclaredMethod("setUpdateUser",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) { try { Method setUpdateTime = entity.getClass().getDeclaredMethod("setUpdateTime",LocalDateTime.class); Method setUpdateUser = entity.getClass().getDeclaredMethod("setUpdateUser",Long.class); // 通过反射为对象赋值 setUpdateTime.invoke(entity, now); setUpdateUser.invoke(entity, currentId); } catch (Exception e) { e.printStackTrace(); } } }
这样就完成了使用注解在执行拦截方法前对参数的属性填充
public interface CategoryMapper { @AutoFill(value = OprationType.INSERT) void insert(category category); }