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

苍穹外卖-菜品新增、删除

批量删除用foreach循环来遍历,删除被套餐关联的SQL语句比较复杂。 

删除菜品表中的菜品数据这里,每次循环需要执行2次SQL,可能会出现性能问题。应该采用如下的SQL形式:delete from dish where id in (?,?,?)。

在sky-server的mapper中已有的DishMapper类中添加如下代码(负责删除菜品):

public Result save(@RequestBody DishDTO dishDTO) 这 1 行代码看起来简单,其实背后把 SpringMVC 参数解析、JSON 反序列化、校验、统一响应 全串在了一起。下面把它拆成 8 个细节逐条细说:


  1. 访问修饰符与返回类型
    public —— 必须 public,Spring 才能用反射调用。
    Result —— 项目里自定义的“统一响应体”,通常长这样

    public class Result<T> {private Integer code;  // 1 成功 0 失败private String msg;private T data;public static <T> Result<T> success(){ ... }
    }

    因此前端收到的总是固定格式,不用再拼 Map。


  1. 方法名
    save 只是方法名,和 URL 没有半毛钱关系;真正的 URL 由类上的 @RequestMapping("/admin/dish") + 方法上的 @PostMapping 拼出:
    POST http://ip:port/admin/dish


  1. @PostMapping 背后做了什么

    1. 限定请求方式 = POST,其他方法(GET/PUT...)进来直接 405。

    2. 继承自 @RequestMapping(method = RequestMethod.POST),Spring 6 之后还支持 @PostMapping(path = "/xxx") 自定义子路径。

    3. 与 REST 风格配套:新增资源 → POST;修改 → PUT;查询 → GET;删除 → DELETE。


  1. @RequestBody 核心流程(面试常问)

    1. 请求进来后,DispatcherServlet 把活交给 RequestResponseBodyMethodProcessor

    2. 处理器先看请求头 Content-Type: application/json(必须是 JSON)。

    3. Jackson(默认)把字节流读成 JsonNode,再按字段名一一映射到 DishDTOsetter/构造器/record 组件

    4. 映射失败(类型对不上、缺失必填字段)会抛 HttpMessageNotReadableException,Spring 默认给 400,项目里可以用 @RestControllerAdvice 捕获后转 Result.error(400,"JSON格式错误")


  1. DishDTO 里常见字段与注解示例

    @Data
    public class DishDTO {private Long id;                // null 表示新增@NotBlankprivate String name;            // 菜品名称@NotNull@Positiveprivate BigDecimal price;       // 价格private String image;           // OSS 图片 URLprivate String description;@Range(min = 0, max = 1)private Integer status;         // 0停售 1起售private List<DishFlavor> flavors;  // 口味列表
    }

    可再加 @Valid 开启校验(Controller 上加 @Validated 即可)。


  1. 事务边界
    Service 层方法 saveWithFlavor 上会有 @Transactional

    • INSERT INTO dish

    • 取到自增主键 id

    • INSERT INTO dish_flavor (dish_id, name, value) VALUES (?,?,?)
      两步任何一步失败整体回滚,保证不会出“菜品成功、口味半截”这种脏数据。


  1. 异常流向

    1. 业务校验失败(如同名菜品已存在)→ 抛自定义 BusinessException → 被全局异常处理器捕获 → 返回 Result.error(msg)

    2. 数据库唯一索引冲突 → Spring 转 DuplicateKeyException → 同样进全局处理器 → 前端收到 0/"菜品已存在"。

    3. 未捕获的运行时异常 → Spring 默认 500 → 也可被同一处理器统一包装成 Result.error(500,"系统异常"),防止堆栈泄露。


  1. 接口文档与测试
    @ApiOperation("新增菜品") 后,启动项目 → 访问 http://ip:port/doc.html(Knife4j)即可看到:

    • 请求方法:POST

    • 请求体示例

      {"name": "辣子鸡","categoryId": 3,"price": 2800,"image": "https://xxx.com/a.jpg","description": "微辣","status": 1,"flavors": [{ "name": "辣度", "value": "[\"微辣\",\"中辣\",\"重辣\"]" }]
      }
    • 响应 200:

      { "code": 1, "msg": null, "data": null }

一句话总结
这行代码把“HTTP 请求 → JSON → Java 对象 → 业务 → 统一响应”全链路打包好,开发者只需关心真正的业务逻辑,其余由 SpringMVC + Jackson + 统一异常处理自动完成。

DishController.java


/*** 菜品管理*/
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
@Slf4j
public class DishController {@Autowiredprivate DishService dishService;/*** 新增菜品* @param dishDTO* @return*/@PostMapping@ApiOperation("新增菜品")public Result save(@RequestBody DishDTO dishDTO){log.info("新增菜品:{}",dishDTO);dishService.saveWithFlavor(dishDTO);return Result.success();}/*** 分页查询* @param dishPageQueryDTO* @return*/@ApiOperation("菜品分页查询")@GetMapping("/page")public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO){log.info("菜品分页查询:{}",dishPageQueryDTO);PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);return Result.success(pageResult);}/*** 批量删除* @param ids* @return*/@ApiOperation("删除菜品")@DeleteMapping()public Result delete(@RequestParam List<Long> ids){log.info("批量删除菜品:{}",ids);dishService.deleteBatch(ids);return Result.success();}

DishService.java

package com.sky.service;
import com.sky.dto.DishDTO;
import com.sky.dto.DishPageQueryDTO;
import com.sky.result.PageResult;
import org.springframework.stereotype.Service;import java.util.List;@Service
public interface DishService {/*** 新增菜品,同时口味* @param dishDTO*/public void saveWithFlavor(DishDTO dishDTO);/*** 菜品分页查询* @param dishPageQueryDTO* @return*/PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO);/*** 删除菜品* @param ids*/void deleteBatch(List<Long> ids);
}

DishServiceImpl.java

package com.sky.service.impl;import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.constant.MessageConstant;
import com.sky.constant.StatusConstant;
import com.sky.dto.DishDTO;
import com.sky.dto.DishPageQueryDTO;
import com.sky.entity.Dish;
import com.sky.entity.DishFlavor;
import com.sky.exception.DeletionNotAllowedException;
import com.sky.mapper.DishFlavorMapper;
import com.sky.mapper.DishMapper;
import com.sky.mapper.SetmealDishMapper;
import com.sky.result.PageResult;
import com.sky.service.DishService;
import com.sky.vo.DishVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.List;@Service
@Slf4j
public class DishServiceImpl implements DishService {@Autowiredprivate DishMapper dishMapper;@Autowiredprivate DishFlavorMapper dishFlavorMapper;@Autowiredprivate SetmealDishMapper setmealDishMapper;/*** 新增菜品,同时保存对应的口味数据*/@Transactionalpublic void saveWithFlavor(DishDTO dishDTO){log.info("新增菜品:{}",dishDTO);Dish dish = new Dish();BeanUtils.copyProperties(dishDTO,dish);dishMapper.insert(dish);Long dishId = dish.getId();//口味List<DishFlavor> flavors = dishDTO.getFlavors();if(flavors != null && flavors.size() > 0){flavors.forEach(dishFlavor -> {dishFlavor.setDishId(dishId);});}//插入dishFlavorMapper.insertBatch(flavors);}/*** 菜品分页查询* @param dishPageQueryDTO* @return*/@Overridepublic PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {PageHelper.startPage(dishPageQueryDTO.getPage(),dishPageQueryDTO.getPageSize());Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);return new PageResult(page.getTotal(),page.getResult());}/*** 菜品批量删除*/@Transactionalpublic void deleteBatch(List<Long> ids){//判断是否删除是否起售中for(Long id :ids){Dish dish = dishMapper.getById(id);if(dish.getStatus() == StatusConstant.ENABLE){//起售throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);}}//是否关联套餐List<Long> setmealIds = setmealDishMapper.getsetmealIdsByDishIds(ids);if(setmealIds != null && setmealIds.size() >0 ){throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);}//删除菜品表中数据for(Long id : ids){dishMapper.deleteById(id);//删除菜品口味表中数据dishFlavorMapper.deleteByDishId(id);}}
}

DishMapper.java

package com.sky.mapper;import com.sky.entity.DishFlavor;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;import java.util.List;@Mapper
public interface DishFlavorMapper {/***  批量插入口味* @param flavors*/void insertBatch(List<DishFlavor> flavors);/*** 根据菜品id删除菜品口味数据* @param dishId*/@Delete("delete from dish_flavor where dish_id = #{dishId}")void deleteByDishId(Long dishId);
}

setMealDishMapper

public interface SetmealDishMapper {/*** 根据菜品id查询对应套餐id* @param setmealDishes*/List<Long> getsetmealIdsByDishIds(List<Long> dishIds);}

setMealDishMapper.xml

<mapper namespace="com.sky.mapper.SetmealDishMapper"><select id="getsetmealIdsByDishIds" resultType="java.lang.Long">select setmeal_id from setmeal_dish where dish_id in<foreach collection="dishIds" item ="dishId" separator="," open="(" close=")">#{dishId}</foreach></select>
</mapper>

DishMapper.xml

<mapper namespace="com.sky.mapper.DishMapper"><insert id="insert" useGeneratedKeys="true" keyProperty="id"parameterType="com.sky.entity.Dish">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><select id="pageQuery" resultType="com.sky.vo.DishVO">select d.*,c.name as categoryName from dish d left outer join category c on c.id = d.category_id<where><if test = "name != null">and d.name like concat('%', #{name},'%')</if><if test = "categoryId != null">and d.categoryId like concat('%', #{categoryId},'%')</if><if test = "status != null">and d.status like concat('%', #{status},'%')</if></where>order by d.update_time desc</select>
</mapper>

http://www.dtcms.com/a/422624.html

相关文章:

  • 如何找到到自己研究领域的经典论文?
  • Kubernetes 健康探针详解
  • 出售东西的网站怎么做建设网站运营成本
  • 【Qt开发】输入类控件(三)-> QComboBox
  • 什么是专用对讲机?遨游专用对讲机有什么优势?
  • 港大和字节携手打造WorldWeaver:以统一建模方案整合感知条件,为长视频生成领域带来质量与一致性双重飞跃。
  • 第三方软件测试公司:【Gatling基于Scala的开源高性能负载测试工具】
  • Social-Auto-Upload - 多平台社交媒体视频自动化上传工具
  • 十一、Hadoop 三种部署模式对比表 组件介绍
  • 十、Hadoop 核心目录功能说明表
  • Leetcode+Java+图论+岛屿问题
  • 容器内部DNS解析针对美国服务器微服务的调试指南
  • 生物科技公司网站模板下载织梦教育咨询企业网站模板
  • 镇江网站建设要多少钱开网店3个月来亏了10万
  • 入侵检测系统(IDS)和入侵防御系统(IPS)
  • 【Youtube】油管上有哪些计算机或互联网历史故事频道?
  • 设计模式:单例模式。饿汉式、懒汉式
  • 如何在项目中选择使用HTTP还是WebSocket?
  • 【代码的暴力美学】-- C语言基础编程题_2
  • GPIO 控制和操作-使用命令通过sysfs文件系统控制GPIO
  • MySQL InnoDB存储引擎表的逻辑存储结构及实现原理详细介绍
  • 网站建设实习周记网站推广建设策略
  • RN 升级react-native 版本
  • RabbitMQ 消费异常:库存不足无法释放的定位与解决
  • 邳州做网站pzwode网站开发的相关技术
  • 精读C++20设计模式——结构型设计模式:代理模式
  • C# 循环和条件用法大全(while dowhile for foreach if Switch try)全站最全
  • 设计模式(C++)详解——中介者模式(3)
  • 小程序原创--基于微信开发者工具实现的猜谜游戏程序
  • 如何进行WGBS的数据挖掘——从甲基化水平到功能通路