当前位置: 首页 > article >正文

外卖开发(三)开发笔记——AOP实现实现公共字段填充、主键回显、抛异常和事务管理

外卖开发(三)开发笔记

  • 一、AOP实现实现公共字段填充(减少重复工作)
    • 实现思路
    • 自定义注解AutoFill
    • 自定义切面AutoFillAspect
    • 在Mapper接口上添加`@AutoFill`注解
  • 二、主键回显情况
  • 三、抛异常 和 事务管理

一、AOP实现实现公共字段填充(减少重复工作)

在这里插入图片描述

实现思路

在这里插入图片描述
1、自定义注解@AutoFill,用于表示需要进行公共字段填充的方法
2、自定义切面类,AutoFillAspect ,统一拦截加入了@AutoFill注解的方法,通过反射为公共字段赋值。
3、在Mapper方法上加入@AutoFill注解,因为这里我们的公共字段是更新时间、更新人、创建时间、创建人,所以只在insert、和update操作时才需要进行AutoFill。在进行Mapper方法前,先将实体类对象的相关属性填充,然后在进行insertupdate(before前置通知)

自定义注解AutoFill

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
     /**
     * 定义注解的value值,对应数据操作类型insert 和 update
     * @return
     */
    OperationType value();
}

自定义枚举

/**
 * 数据库操作类型
 */
public enum OperationType {

    /**
     * 更新操作
     */
    UPDATE,

    /**
     * 插入操作
     */
    INSERT

}

自定义切面AutoFillAspect

/**
 * 自定义通知类,实现公共字段填充
 */
@Aspect
@Component
@Slf4j
public class AutoFillAspect {

    @Pointcut("@annotation(com.sky.annotation.AutoFill)")
    public void pointCut(){}

    @Before("pointCut()")
    public void autoFill(JoinPoint joinPoint) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        log.info("开始进行字段填充");

        //获取当前被拦截方法的数据库操作类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
        OperationType operationType = autoFill.value();
        //获取当前方法的参数--实体类对象   反射
        Object[] pointArgs = joinPoint.getArgs();
        if(pointArgs[0] == null){
            return;
        }
        Object entity = pointArgs[0];

        LocalDateTime now = LocalDateTime.now();
        Long currentId = BaseContext.getCurrentId();
        //根据不同的操作类型,为对应的属性通过反射来赋值
        if(operationType == OperationType.INSERT){
            Method setCreateTime = 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);

            //反射为属性赋值
            setCreateTime.invoke(entity,now);
            setCreateUser.invoke(entity,currentId);
            setUpdateTime.invoke(entity,now);
            setUpdateUser.invoke(entity,currentId);
        }
        else if(operationType == OperationType.UPDATE){

            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);
        }

    }
}

在Mapper接口上添加@AutoFill注解

在这里插入图片描述
此时,我们就不需要在service中重复进行字段的填充。

二、主键回显情况

如:新增菜品
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

涉及到了两张表,dish表和dish_flavor表。

dish表
在这里插入图片描述

dish_flavor表

在这里插入图片描述
DishDTO.java

public class DishDTO implements Serializable {

    private Long id;
    //菜品名称
    private String name;
    //菜品分类id
    private Long categoryId;
    //菜品价格
    private BigDecimal price;
    //图片
    private String image;
    //描述信息
    private String description;
    //0 停售 1 起售
    private Integer status;
    //口味
    private List<DishFlavor> flavors = new ArrayList<>();

}

分析:新增dish操作,需要进行两次insert,分别插入dish表和dish_flavor表,但是我们从前端接收到的数据中(DishDTO)List<DishFlavor>中只包含了我们新增的口味名称口味值,并不会包含对应的dish_id,那么我们就需要把第一次向dish表中插入新数据时自动生成的id回显(带回来),并付给List<DishFlavor>中的dish_id。

DishService.java

/**
     * 新增菜品
     * @param dishDTO
     */
    @Override
    public void insertDish(DishDTO dishDTO) {
        Dish dish = new Dish();
        BeanUtils.copyProperties(dishDTO,dish);
        dishMapper.insert(dish);
        //获取inser之后的主键
        Long dishId = dish.getId();

        List<DishFlavor> flavors = dishDTO.getFlavors();
        if(flavors != null && flavors.size()>0){
            flavors.forEach(dishFlavor -> {
                dishFlavor.setDishId(dishId);  //将回显的主键id赋给flavir中的dish_id
            });
            dishFlavorMapper.insertBatch(flavors);
        }
    }
	/**
     * 批量新增dish
     * @param flavors
     */
 	@AutoFill(OperationType.INSERT)
    void insert(Dish dish);
 	/**
     * 批量新增dish_flavor
     * @param flavors
     */
    void insertBatch(List<DishFlavor> flavors);
<!--插入新菜品-->
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">  <!--主键回显-->
        insert into dish (name,category_id,price,image,description,status,create_time,update_time,create_user,update_user)
        values
        (#{name},#{categoryId},#{price},#{image},#{description},#{status},#{createTime},#{updateTime},#{createUser},#{updateUser})
    </insert>

三、抛异常 和 事务管理

在删除某个菜品的时候,如果这个菜品的状态为起售(status=1)时,就无法进行删除,需要停止删除操作,并抛出相关异常。如果这个菜品正在被一些套餐关联,那么也不能删除。

在删除菜品的时候,也要删除相关的菜品对应的口味,所以需要删除两个表。显而易见,我们需要进行事务管理

/**
     * 批量删除菜品
     * @param ids
     */
    @Override
    @Transactional  //开启事务
    public void deleteDish(List<Long> ids) {
        //是否存在起售中的 存在就不能删除
        //利用count(1)计算出准备删除的菜品中status=1 的数量,如果大于0,说明存在起售中的
        Integer status = dishMapper.queryStatus(ids); 
        if (status > 0)
        {
        	//抛出自定义的异常和异常信息:无法删除存在起售的菜品
            throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
        }
        //查询有没有关联套餐 关联就不能删除
        //使用count(1)查询套餐中对应dish是否存在,如果大于0则不能删除
        Integer integer = setmealDishMapper.countDish(ids);
        if(integer > 0){
        	//抛出自定义的异常和异常信息:无法删除存在关联的菜品
            throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
        }
        //删除dish表中的数据
        dishMapper.deleteDish(ids);
        //删除dish-flavor表
        dishFlavorMapper.deleteByDishId(ids);
    }

http://www.kler.cn/a/421934.html

相关文章:

  • 基于TensorFlow框架的线性回归实现
  • 重生之我在异世界学编程之C语言:选择结构与循环结构篇
  • T113-S3 Tina 新增自定义板型号
  • 在 MacOS 上为 LM Studio 更换镜像源
  • C# 动态类型 Dynamic
  • CSS学习记录03
  • Matlab图像处理——基于内容的图像检索GUI
  • 基于云模型的车辆行驶速度估计算法matlab仿真
  • 【C++】数组
  • jmeter 获取唯一全局变量及多线程读写的问题
  • JavaScript实现tab栏切换
  • 从零开始搭建图像去雾神经网络
  • React基础知识三 router路由全指南
  • springboot/ssm高校线上心理咨询室系统Java大学生心理健康咨询平台web源码
  • 用micropython 操作stm32f4单片机的定时器实现蜂鸣器驱动
  • 【数据结构】队列的概念、结构和实现详解
  • 【layui】 自己编写的可输入下拉框
  • HCIA笔记6--路由基础与静态路由:浮动路由、缺省路由、迭代查找
  • Ubuntu WiFi检测
  • CLIP-MMA: Multi-Modal Adapter for Vision-Language Models
  • Go-知识依赖管理2
  • 力扣【算法学习day.50】
  • go语言的成神之路-筑基篇-并发
  • 亚马逊云(AWS)使用root用户登录
  • MySQL算法篇(一)
  • html button 按钮单选且 高亮