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

外卖开发(三)开发笔记

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

实现思路

1、自定义注解@AutoFill,用于表示需要进行公共字段填充的方法

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

3、在Mapper方法上加入@AutoFill注解,因为这里我们的公共字段是更新时间、更新人、创建时间、创建人,所以只在insert、和update操作时才需要进行AutoFill。在进行Mapper方法前,先将实体类对象的相关属性填充,然后在进行insertupdate(before前置通知)

自定义注解AutoFill

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

自定义枚举

java 复制代码
/**
 * 数据库操作类型
 */
public enum OperationType {

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

    /**
     * 插入操作
     */
    INSERT

}

自定义切面AutoFillAspect

java 复制代码
/**
 * 自定义通知类,实现公共字段填充
 */
@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

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

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);
        }
    }
java 复制代码
	/**
     * 批量新增dish
     * @param flavors
     */
 	@AutoFill(OperationType.INSERT)
    void insert(Dish dish);
 	/**
     * 批量新增dish_flavor
     * @param flavors
     */
    void insertBatch(List<DishFlavor> flavors);
xml 复制代码
<!--插入新菜品-->
    <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)时,就无法进行删除,需要停止删除操作,并抛出相关异常。如果这个菜品正在被一些套餐关联,那么也不能删除。

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

java 复制代码
/**
     * 批量删除菜品
     * @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);
    }
相关推荐
大筒木老辈子2 小时前
Linux笔记---进程:进程替换
linux·服务器·笔记
不会聊天真君6473 小时前
爬虫(JAVA笔记第四十期)
java·笔记
吃着火锅x唱着歌4 小时前
PHP7内核剖析 学习笔记 第一章 PHP基础架构
笔记·学习·php
亦枫Leonlew7 小时前
三维测量与建模笔记 - 5.3 光束法平差(Bundle Adjustment)
笔记·计算机视觉·三维重建·光束法平差
Lbs_gemini06037 小时前
C++研发笔记14——C语言程序设计初阶学习笔记12
c语言·开发语言·c++·笔记·学习
MarisolHu8 小时前
前端学习笔记-Vue篇-02
前端·vue.js·笔记·学习
先知demons10 小时前
uniapp开发微信小程序笔记10-触底加载
前端·笔记·微信小程序·小程序·uni-app
我的老子姓彭10 小时前
C++学习笔记
c++·笔记·学习
m0_6896182812 小时前
亚毫米级纤维机器人,如何在腔内“大显身手”
网络·笔记·机器人