外卖开发(三)开发笔记——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);
    }
相关推荐
_x_w42 分钟前
【12】数据结构之基于线性表的排序算法
开发语言·数据结构·笔记·python·算法·链表·排序算法
s_little_monster1 小时前
【Linux】线程控制函数
linux·运维·服务器·经验分享·笔记·学习·学习方法
kfepiza1 小时前
硬盘分区格式之GPT(GUID Partition Table)笔记250406
linux·windows·笔记·gpt
十年之少1 小时前
粘性定位(position:sticky)——微信小程序学习笔记
笔记·学习·微信小程序
切图只会helloworld1 小时前
RabbitMQ笔记
笔记
!!!5252 小时前
MongoDB 新手笔记
数据库·笔记·mongodb
姝孟2 小时前
Linux学习笔记 1
linux·笔记·学习
dg10112 小时前
go-zero学习笔记(六)---gozero中间件介绍
笔记·学习·golang
编程老菜鸡3 小时前
计算机网络笔记-分组交换网中的时延
笔记·计算机网络·智能路由器
Better Rose11 小时前
蓝桥杯备赛学习笔记:高频考点与真题预测(C++/Java/python版)
笔记·学习·蓝桥杯