SpringBoot项目中通过自定义注解方式解决Mysql数据库公共字段填充问题

一、问题背景。

在常见的后台管理系统中,每张表的公共字段通常有create_time, update_time等,我们在使用MyBatis操作这些表进行增删改查的过程中,每次都学要对这些字段进赋值。

而针对于这些字段,我们的赋值方式为:

1). 在新增数据时, 将createTimeupdateTime 设置为当前时间, createUser、updateUser设置为当前登录用户ID。

2). 在更新数据时, 将updateTime 设置为当前时间, updateUser设置为当前登录用户ID。

目前,在我们的项目中处理这些字段都是在每一个业务方法中进行赋值操作,如下:

/**
     * 新增员工
     *
     * @param employeeDTO
     */
    public void save(EmployeeDTO employeeDTO) {
        //.......................
        //设置当前记录的创建时间和修改时间
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());

        //设置当前记录创建人id和修改人id
        employee.setCreateUser(BaseContext.getCurrentId());//目前写个假数据,后期修改
        employee.setUpdateUser(BaseContext.getCurrentId());
        employeeMapper.insert(employee);
    }

如果都按照上述的操作方式来处理这些公共字段, 需要在每一个业务方法中进行操作, 编码相对冗余、繁琐,那能不能对于这些公共字段在某个地方统一处理,来简化开发呢?

**答案是可以的,我们使用AOP切面编程,实现功能增强,来完成公共字段自动填充功能。

二、实现思路

在实现公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码。在上述的问题分析中,我们提到有四个公共字段,需要在新增/更新中进行赋值操作, 具体情况如下:

序号字段名含义数据类型操作类型
1create_time创建时间datetimeinsert
2create_user创建人idbigintinsert
3update_time修改时间datetimeinsert、update
4update_user修改人idbigintinsert、update

实现步骤:

(1). 自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法

(2). 自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值

(3). 在 Mapper 的方法上加入 AutoFill 注解

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

2.1 自定义注解注解@AutoFill

/**
  * 自定义注解,用于标识某个方法需要进行功能字段自动填充处理
  */
@Target(Element.METHOD) // 中该注解作用域
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
    // 数据库操作类型: UPDATE, INSERT
    OperationType value();
}

2.2 定义枚举

/**
  * 数据库操作类型
  */
public enum OperationType {
    UPDATE, // 更新操作
    INSERT  // 插入操作
}

2.3 定义切面类

@Aspect
@Component
@Slf4j
public class AutoFillAspect{
    /** 
      * 切入点
      */
    @PointCut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.nuc.annotation.AutoFill)")
    public void autoFillPointCut(){}

    /**
      * 前置通知
      */
    @Before("autoFillPointCut()")
    public void autoFill(JointPoint jointPoint) {
        log.info("快开始进行公共字段填充....");
    }
}

2.4完善切面类中autoFill方法

@Before
public void autoFill(JointPoint jointPoint) {
    // 获取被迁至方法拦截的书库操作类型
    MethodSignature methodSignature = (MethodSignature) jointPoint.getSignature(); // 方法签名对象
    AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class) // 获得方法上的注解对象。
    OperationType operationType = autoFill.value();

    // 获得被当前方法拦截的方法参数--实体对象
    Object[] args= jointPoint.getArgs();
    if(args == null || args.length == 0) {
        return;
    }

    Object entity = arg[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){
            //为2个公共字段赋值
            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();
            }
        }
}

2.5在Mapper接口的方法上加入 AutoFill 注解

@Mapper
public interface CategoryMapper {
    /**
     * 插入数据
     * @param category
     */
    @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 insert(Category category);
    /**
     * 根据id修改分类
     * @param category
     */
    @AutoFill(value = OperationType.UPDATE)
    void update(Category category);

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值