苍穹外卖|学习笔记|day07

发布于:2025-06-10 ⋅ 阅读:(22) ⋅ 点赞:(0)

博主个人背景:双非本211硕 研一下。深知自身水平较差 算法无戏,自学java的求生之路

计划每日学习,完成苍穹外卖的学习。借此博客写明学习遇到的问题以及解决方案,同大家一起交流学习。

image-20250520105301407

公共字段自动填充[2025.6.6]

1.问题分析:

公共字段:在我们的业务表当中有创建人/时间、修改人/时间,我们经常会对这四个公共字段来进行赋值修改,这样就会存在很多重复性的代码。【如图所示:前面开发的员工、分类相关公共字段代码】从而导致代码冗余,不便于后期维护

为了避免重复性的操作,需要进行相关代码的编写从而解决公共字段的自动填充。

image-20250606112038257

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进行修改

image-20250609140658826

小节遇到的问题/疑问点/难点:

​ 对于本小节所使用的AOP、枚举类等等所涉及到的知识点将在后面一节进行重新学习。故不在本节展示。