博主个人背景:双非本211硕 研一下。深知自身水平较差 算法无戏,自学java的求生之路
计划每日学习,完成苍穹外卖的学习。借此博客写明学习遇到的问题以及解决方案,同大家一起交流学习。
公共字段自动填充[2025.6.6]
1.问题分析:
公共字段:在我们的业务表当中有创建人/时间、修改人/时间,我们经常会对这四个公共字段来进行赋值修改,这样就会存在很多重复性的代码。【如图所示:前面开发的员工、分类相关公共字段代码】从而导致代码冗余,不便于后期维护
为了避免重复性的操作,需要进行相关代码的编写从而解决公共字段的自动填充。
2.实现思路:
可以通过切面来拦截持久层的相关操作,来为公共字段进行赋值。
3.代码开发:
(1)设定自定义注解
我们在com.sky当中新建一个文件夹Annotation,存放我们的自定义注解.
在com.sky当中新建一个文件夹aspect,存放我们的切面类
@Target(ElementType.METHOD)//表示我们的注解只能够添加在方法上
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
//使用自定义枚举类,指定数据库操作类型【update insert只有这两种方法会使用到公共字段】
OperationType value();
}
package com.sky.enumeration;
/**
* 数据库操作类型
*/
public enum OperationType {
/**
* 更新操作
*/
UPDATE,
/**
* 插入操作
*/
INSERT
}
(2)设定自定义切面类
@Aspect //表明为切面类
@Component //其也是一个Bean,提供给容器管理
@Slf4j
public class AutoFillAspect {
//定义 通知 + 切入点
/**
* 切入点[对哪些类的哪些方法进行拦截]
*/
//@Pointcut("execution(* com.sky.mapper.*.*(..))")//拦截mapper下所有的类的所有的方法。
//但是有些方法我们是不需要拦截的,我们只需要拦截带有自定义注解@AutoFill的方法。
// 拦截到之后我们需要做什么事情呢?需要对公共字段进行赋值---相关操作写到通知当中
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")//拦截mapper下所有的类的所有的方法。
public void autoFillPointcut(){}
/**
* 前置通知,在sql执行之前,进行拦截操作
* 在通知当中进行公共字段的赋值。
*/
@Before("autoFillPointcut()")//设置切入点
public void autoFill(JoinPoint joinPoint){//需要传入参数:连接点【哪个方法被拦截到了,以及被拦截到的方法的具体参数值、类型】
log.info("开始进行公共字段的自动填充\n...");
//获取到当前被拦截的方法上的数据库的操作类型
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//向下转型为MethodSignature
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个公共字段赋值
//这个地方要复制,需要通过反射来进行赋值,代码是存在相应的get、set方法的。
try {
//首先我们需要获得相应的set方法
//指定方法名称,方法参数类型
//Method setCreteTime = entity.getClass().getDeclaredMethod("setCreteTime", 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);
//这里为了拼写更规范,而且为了避免拼写出错,这里我们将相应的方法进行封装成常量。
Method setCreteTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//通过反射为具体的属性赋值
setCreteTime.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();
}
}
}
}
(3)在Mapper的方法上加入AutoFill注解
[这里只以CategoryMapper.java为例]
@Mapper
public interface CategoryMapper {
@Insert("insert into category(type,name,sort,status,create_time,update_time,create_user,update_user)" +
"values (#{type},#{name},#{sort},#{status},#{createTime},#{updateTime},#{createUser},#{updateUser}) ")
@AutoFill(value = OperationType.INSERT)
void save(Category category);
@Delete("delete from category where id = #{id}")
void delete(Long id);
Page<Category> pageQuery(CategoryPageQueryDTO categoryPageQueryDTO);
@AutoFill(value = OperationType.UPDATE)
void update(Category category);
@Select("select * from category where type = #{type}")
List<Category> list(Integer type);
}
4.功能测试
数据库当中相应updateTime进行修改
小节遇到的问题/疑问点/难点:
对于本小节所使用的AOP、枚举类等等所涉及到的知识点将在后面一节进行重新学习。故不在本节展示。