苍穹外卖-day03-修改菜品模块学习笔记

0 阅读6分钟

苍穹外卖-day03-修改菜品模块学习笔记

本文适合正在做 Java 实战项目、需要掌握分层开发思想、属性拷贝与动态SQL语句使用的开发小白,能帮你快速理解正确修改的实现思路

【作者说:在这一模块中,我又深入学习了分层开发的思想,并且深入理解了菜品id的两种查询方式----一种是视频教学的属性拷贝,另一种是拓展的SQL关联查询,并且在文章里也着重讲了在什么情况下最优解是什么,有助于初学者在具体环境背景下深度理解,以下文章的引用部分是我自己的思考与见解,希望对你们有用!留下关注和点赞,我将继续更新本专栏内容!!! 】


一、分层开发思想与职责划分

在项目开发中采用分层开发思想,相比将 VO 封装逻辑写在 Controller 层,本项目将业务逻辑抽取到 Service 层,让各层职责更单一清晰:

Controller 层:仅负责接收前端请求、调用 Service、返回响应结果 Service 层:专注处理核心业务逻辑,包括数据封装、业务校验、数据处理 Mapper 层:负责与数据库交互,执行 SQL 语句完成数据增删改查

分层开发的思想:

视频里把 VO 的封装逻辑写在 Controller 层,而我们现在的项目把这部分逻辑抽到了 Service 层实现,这样Controller 只负责接收请求和返回响应,Service 专注处理业务逻辑,代码的职责更清晰,后续维护和扩展也更方便。

我的见解:

针对于这个接口开发,首先需要在Controller层写方法的基础逻辑getById,在这里调用dishService里面的方法dishService.getById(id);然后在dishService接口里声明这个方法DishVO.getById(Long id);最后在这个DishServiceImpl里写具体的操作实现方法。


二、根据 ID 查询菜品(修改前数据回显)

  1. Controller 层编写 编写getById方法,接收前端传入的菜品 id,调用 Service 层方法获取菜品详情并返回:
/**
 * 根据id查询菜品详情
 * @param id 菜品id
 * @return 菜品VO对象
 */
@GetMapping("/{id}")
public Result<DishVO> getById(@PathVariable Long id) {
    DishVO dishVO = dishService.getById(id);
    return Result.success(dishVO);
}
  1. Service 层接口定义 在DishService接口中声明根据 id 查询菜品的方法:
/**
 * 根据id查询菜品及口味信息
 * @param id 菜品id
 * @return DishVO
 */
DishVO getById(Long id);
  1. ServiceImpl 层实现 在DishServiceImpl中完成具体业务逻辑,封装 DishVO 对象并返回。
 /**
     *根据id查询菜品
     * @param id
     * @return
     */
    @Override//这里就会体现重写的好处,我不用重新写方法具体内容,只需要进行功能的拓展
    public DishVO getById(Long id) {
        log.info("查询菜品:{}",id);
        DishVO dishVO = dishMapper.getById(id);
        dishVO.setFlavors(flavorMapper.getByDishId(id));

        return dishVO;
    }

三、菜品数据拷贝:BeanUtils.copyProperties 使用

  1. DTO 与实体类作用区分 DishDTO:前端传递给后端的参数对象,包含菜品基础信息 + 口味信息 Dish:与数据库表字段一一对应的实体类,用于操作数据库
  2. 属性拷贝逻辑 DTO 不能直接用于数据库操作,需通过工具类完成属性拷贝:

关于属性拷贝:要对菜品表基本信息进行修改首先需要的得到菜品信息,菜品基本信息存放在DishDTO这个类里面,传参时需要新建一个dishDTO来作为这个类的实例对象进行具体操作,但这只是一个装有信息的说明书,不能在上面进行修改,所以需要新建一个变量来进行操作Dish dish = new Dish();copyProperties(dishDTO,dish);然后在这个变量里面进行拷贝,把dishDTO里面的内容拷贝到dish里。

// 新建Dish对象
Dish dish = new Dish();
// 将DishDTO中同名、同类型属性拷贝到Dish中
BeanUtils.copyProperties(dishDTO, dish);

Dish与DishDTO这两个类的实例对象的拷贝不是简单的合并,要理解它首先需要知道DishDTO是前端传给后端的信息,Dish的信息是与数据库表里的的信息一一对应的,所以这个拷贝是将DishDTO里面有用的内容传给Dish,让它更新数据表中的信息,做到一个修改的操作,而没有用的信息就不用管它

  1. 拷贝特性说明

BeanUtils.copyProperties 会自动把 DTO 和 Dish 类中字段名、类型都匹配的属性拷贝过去,DTO 里多的 flavors 这种字段会被自动忽略。BeanUtils.copyProperties 是浅拷贝,像口味这种嵌套的集合属性不会被拷贝,刚好符合我们只更新基础菜品信息、单独处理口味的业务逻辑

  • 自动匹配字段名 + 字段类型相同的属性,多余字段(如 flavors)自动忽略
  • 属于浅拷贝,嵌套集合对象不会拷贝,刚好满足菜品基础信息与口味信息分开处理的业务需求
  • 实现前端参数到数据库实体的转换,为后续修改操作做数据准备

四、菜品修改功能实现逻辑

  1. 接收前端传入的DishDTO参数
  2. 通过BeanUtils.copyProperties将 DTO 属性拷贝至 Dish 实体
  3. 调用 Mapper 层方法更新菜品基础信息
  4. 单独处理菜品口味数据,实现口味的新增、删除、修改
  5. 事务控制保证数据一致性

五、Mapper 层 SQL 编写规范

  • 注解 SQL 与 XML 动态 SQL 选择

我发现在DishMapper接口里面写简单查询语句,比如 @Select("select * from dish where category_id = #{categoryId} and status=1"),但是在DishMapper.xml文件里写复杂的动态SQL语句,这是注解式与XML式的区别,同一个 SQL,二选一,不能两边都写,也不能两边都不写

简单 SQL:单表查询、无复杂条件,使用@Select注解写在 Mapper 接口上,简洁高效

@Select("select * from dish where category_id = #{categoryId} and status=1")
List<Dish> getByCategoryId(Long categoryId);

复杂 SQL:多表联查、动态 SQL(if/foreach)、批量操作,写在Mapper.xml中,结构清晰易维护

 </insert>
    <update id="update">
            update dish
            <set>
                            <if test="name != null"> name = #{name},</if>
                            <if test="categoryId != null">category_id = #{categoryId},</if>
                            <if test="price != null"> price = #{price},</if>
                            <if test="image != null"> image = #{image}, </if>
                            <if test="description != null">description = #{description}, </if>
                            <if test="status != null"> status = #{status}, </if>
                            <if test="update_time != null">update_time = #{updateTime},</if>
                            <if test="update_user != null">update_user = #{updateUser}</if>
            </set>
            where id = #{id}
    </update>

注意事项:同一条 SQL 只能二选一,不可接口与 XML 同时编写


六、笔记总结

  • 本文围绕苍穹外卖修改菜品模块,梳理了核心开发逻辑与关键知识点。首先明确分层开发职责,将 VO 封装逻辑抽至 Service 层,使各层职责清晰、便于维护;接着阐述根据菜品 ID 查询的全流程,从 Controller 层调用、Service 接口声明到 ServiceImpl 具体实现;详解 BeanUtils.copyProperties 的使用场景、逻辑及特性,区分 Dish 与 DishDTO 的作用;梳理菜品修改的完整步骤,强调事务控制的重要性;最后明确 Mapper 层 SQL 编写规范,区分注解与 XML 的适用场景。整体覆盖修改菜品的核心逻辑与关键技术,助力开发者掌握分层开发与动态 SQL 的实际应用。