SpringBoot新增菜品模块开发(事务管理+批量插入+主键回填)

需求分析与设计

一:产品原型

后台系统中可以管理菜品信息,通过 新增功能来添加一个新的菜品,在添加菜品时需要选择当前菜品所属的菜品分类,并且需要上传菜品图片。

新增菜品原型:

当填写完表单信息, 点击"保存"按钮后, 会提交该表单的数据到服务端, 在服务端中需要接受数据, 然后将数据保存至数据库中。

业务规则:

  • 菜品名称必须是唯一的

  • 菜品必须属于某个分类下,不能单独存在

  • 新增菜品时可以根据情况选择菜品的口味

  • 每个菜品必须对应一张图片

二:接口设计

根据上述原型图先**粗粒度**设计接口,共包含3个接口。

- 根据类型查询分类(已完成)
- 文件上传(已完成)
- 新增菜品

接下来明确接口的请求方式、请求路径、传入参数和返回值。

口味非必须,且是object[]数组,可以传多个口味(一个菜品可以对应多种口味)

所以后端使用集合封装

三:数据表设计

新增菜品,其实就是将新增页面录入的菜品信息插入到dish表,如果添加了口味做法,还需要向dish_flavor表插入数据。所以在新增菜品时,涉及到两个表:

表名说明
dish菜品表
dish_flavor菜品口味表

1). 菜品表:dish

字段名数据类型说明备注
idbigint主键自增
namevarchar(32)菜品名称唯一
category_idbigint分类id逻辑外键
pricedecimal(10,2)菜品价格
imagevarchar(255)图片路径
descriptionvarchar(255)菜品描述
statusint售卖状态1起售 0停售
create_timedatetime创建时间
update_timedatetime最后修改时间
create_userbigint创建人id
update_userbigint最后修改人id

2). 菜品口味表:dish_flavor

字段名数据类型说明备注
idbigint主键自增
dish_idbigint菜品id逻辑外键
namevarchar(32)口味名称
valuevarchar(255)口味值

 代码开发

一:设计DTO类

package com.sky.dto;

import com.sky.entity.DishFlavor;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Data
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<>();
}

细节:

注意口味用List集合封装,因为前端可以传递多种口味数据(对应口味实体类)

口味实体类如下:

/**
 * 菜品口味
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DishFlavor implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;
    //菜品id
    private Long dishId;

    //口味名称
    private String name;

    //口味数据list
    private String value;

}

二:Controller层

package com.sky.controller.admin;

import com.sky.dto.DishDTO;
import com.sky.dto.DishPageQueryDTO;
import com.sky.entity.Dish;
import com.sky.result.PageResult;
import com.sky.result.Result;
import com.sky.service.DishService;
import com.sky.vo.DishVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Set;

/**
 * 菜品管理
 */
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
@Slf4j
public class DishController {

    @Autowired
    private DishService dishService;

    /**
     * 新增菜品
     *
     * @param dishDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增菜品")
    public Result save(@RequestBody DishDTO dishDTO) {
        log.info("新增菜品:{}", dishDTO);
        dishService.saveWithFlavor(dishDTO);//后绪步骤开发
        return Result.success();
    }
}

细节:

注意接口文档上显示返回Result中data是非必须的,所以Result后可以不加泛型。

三:Service类(事务、forEach循环)

package com.sky.service.impl;


@Service
@Slf4j
public class DishServiceImpl implements DishService {

    @Autowired
    private DishMapper dishMapper;
    @Autowired
    private DishFlavorMapper dishFlavorMapper;

    /**
     * 新增菜品和对应的口味
     *
     * @param dishDTO
     */
    @Transactional
    public void saveWithFlavor(DishDTO dishDTO) {

        Dish dish = new Dish();
        BeanUtils.copyProperties(dishDTO, dish);

        //向菜品表插入1条数据
        dishMapper.insert(dish);//后绪步骤实现

        //获取insert语句生成的主键值
        Long dishId = dish.getId();

        List<DishFlavor> flavors = dishDTO.getFlavors();
        if (flavors != null && flavors.size() > 0) {
            flavors.forEach(dishFlavor -> {
                dishFlavor.setDishId(dishId);
            });
            //向口味表插入n条数据
            dishFlavorMapper.insertBatch(flavors);//后绪步骤实现
        }
    }

}

细节:

  • Service层别忘记要把DTO转换为实体对象
    • 利用BeanUtils提供的copyProperties方法可以快速转换
  • 因为新增菜品需要操作两张数据表(菜品表和口味表),所以需要原子操作,于是定义事务
    • @Transactional注解
  • 又因为口味表需要菜品表的主键(菜品id),而菜品id是数据库负责维护的自增主键,所以需要mybatis配合进行主键回填
    • Long dishId = dish.getId();成功的前提是mapper层set了id
  • 因为口味是非必须的请求数据,所以首先需要判断集合是否为空
    • 之后需要为口味对象赋值菜品id
    • 最后批量的插入口味表
  • forEach方法的使用
    • 集合.forEach(元素名->{元素所进行的操作})

四:Mapper层(主键回填、批量删除)

dishMapper:

	/**
     * 插入菜品数据
     *
     * @param dish
     */
    @AutoFill(value = OperationType.INSERT)
    void insert(Dish dish);

对应的xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.DishMapper">

    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        insert into dish (name, category_id, price, image, description, create_time, update_time, create_user, update_user,status)
        VALUES (#{name},#{categoryId},#{price},#{image},#{description},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})
    </insert>
</mapper>

细节:

这行定义了主键回填:<insert id="insert" useGeneratedKeys="true" keyProperty="id">,会将id的值返回到参数列表的Dish对象dish的id属性中

dishFlavorMapper:

package com.sky.mapper;

import com.sky.entity.DishFlavor;
import java.util.List;

@Mapper
public interface DishFlavorMapper {
    /**
     * 批量插入口味数据
     * @param flavors
     */
    void insertBatch(List<DishFlavor> flavors);

}

对应的XML:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.DishFlavorMapper">
    <insert id="insertBatch">
        insert into dish_flavor (dish_id, name, value) VALUES
        <foreach collection="flavors" item="df" separator=",">
            (#{df.dishId},#{df.name},#{df.value})
        </foreach>
    </insert>
</mapper>

细节:

批量插入

        <foreach collection="参数列表中传递过来的集合名" item="元素名称(自定义)" separator="(下列元素的属性之间分割的分隔符)">
            (#{df.dishId},#{df.name},#{df.value})
        </foreach>

形成的效果

(  ,   ,   ,)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/543095.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python里安装了库却报错找不到是怎么回事?

你在写代码的时候有没有遇到过这样的问题&#xff1a; 明明已经用pip安装好了一个Python模块&#xff0c; 但当你在代码中使用时&#xff0c;却给你报错说找不到这个库。 出现这种情况&#xff0c;绝大多数都是因为你安装模块的那个pip&#xff0c;和你执行代码时的python&…

【数据分析】嫡权法EWM

总结&#xff1a;基于熵值信息来计算出权重&#xff0c;数据具有客观性。 目录 简介 计算步骤 案例 简介 熵值法原理 熵值法的基本思路是根据指标变异性的大小来确定客观权重信息熵:信息量的期望。可以理解成不确定性的大小&#xff0c;不确定性越大&#xff0c;信息熵也就…

js脚本解决因挂VPN导致boss上高德地图无法正常规划公交路线问题

​ 情况说明&#xff1a; 最开始一直以为boss上的查询自己所在地点到面试地点的公交的功能有bug,总是规划路线失败&#xff0c;结果这个一调试&#xff0c;发现是自己的经纬度有问题导致的。 程序猿嘛&#xff0c;开机VPN必须是挂着的&#xff0c;这就导致了boss获取到了错误…

Python中合并列表的五种方法!

在 Python 编程中&#xff0c;我们经常需要将两个或多个列表合并为一个。这个过程通常是为了数据处理或者进行更复杂的操作。 列表合并是一个将两个或多个列表的元素整合到一起的过程。Python 提供了多种方式来实现这一点&#xff0c;每种方式都有自己的应用场景。 一、使用 …

SpringBoot编写一个SpringTask定时任务的方法

1&#xff0c;在启动类上添加注解 EnableScheduling//开启定时任务调度 2&#xff0c; 任务&#xff08;方法&#xff09;上也要添加注解&#xff1a; Scheduled(cron " 0 * * * * ? ") //每分钟执行一次 域&#xff1a; 秒 分 时 日 月 周 &#xff08;年&#…

内网渗透-windows权限维持的方法

windows权限维持的方法 文章目录 windows权限维持的方法一、影子账户二、粘滞键后门三、logon scripts后门五、注册表自启动后门六、屏幕保护程序后门七、计划任务后门八、服务自启动后门九、黄金票据十、白银票据十二、bitsadmin十五、CLR劫持 一、影子账户 1.使用如下命令创…

微信小程序开发遇到的奇奇怪怪的问题

新建项目发现顶部栏标题不生效问题 开发者工具新建项目默认开启全局Skyline渲染引擎&#xff0c;因为Skyline不支持原生导航栏&#xff0c;所以就没显示原生导航栏了。 如果想用回原生导航栏&#xff0c;可以把app.json里面的 "renderer": "skyline", 去掉…

2D AI交互数字人:赋能文旅、金融、政务、教育行业数字化转型

AI交互数字人结合了语音合成、语音识别、语义理解、图像处理、机器翻译、虚拟形象驱动等多项AI核心技术&#xff0c;可以提供服务导览、业务咨询、语音互动交流、信息播报等智能服务。 其中&#xff0c;2D AI交互数字人是采集真人视频&#xff0c;通过AI训练&#xff0c;生成逼…

竞赛 基于CNN实现谣言检测 - python 深度学习 机器学习

文章目录 1 前言1.1 背景 2 数据集3 实现过程4 CNN网络实现5 模型训练部分6 模型评估7 预测结果8 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于CNN实现谣言检测 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&am…

ELFK日志分析系统之搭建ELF+Filebeaat+Zookeeper+Kafka

引言 结合前面所学 http://ELK日志分析系统 一、为什么要做日志分析平台 随着业务量的增长&#xff0c;每天业务服务器将会产生上亿条的日志&#xff0c;单个日志文件达几个GB&#xff0c;这时我们发现用Linux自带工具&#xff0c;cat grep awk 分析越来越力不从心了&#…

从0到1实现RPC | 11 丰富测试案例

测试案例主要针对服务消费者consumer&#xff0c;复杂逻辑都在consumer端。 常规int类型&#xff0c;返回User对象 参数类型转换&#xff0c;主要实现逻辑都在TypeUtils工具类中。 测试方法重载&#xff0c;同名方法&#xff0c;参数不同 方法签名的实现&#xff0c;主要逻辑…

RHCE--dns正反向解析小实验

一、准备工作 1.关闭防火墙 [rootserver ~]# setenforce 0 [rootserver ~]# systemctl stop firewalld 2.安装软件 [rootserver ~]# yum install bind -y 二、正向解析 服务端IP客户端IP网址192.168.203.128192.168.203.130www.openlab.com 服务端配置静态ip [root…

蓝桥杯嵌入式第十五届省赛真题题目

蓝桥杯昨天也考完了&#xff0c;大家可以看看题目 客观题题目 程序题题目

遥感卫星:探索地球的科技之旅

遥感卫星是人类探索地球、理解地球、保护地球的重要工具&#xff0c;其发展历程承载了人类对地球的探索与认知的历程。从最初的概念到如今的高科技应用&#xff0c;遥感卫星技术的发展见证了人类科技的不断进步与创新。 初心萌芽&#xff1a; 遥感卫星的发展始于20世纪中叶&…

AGV小车导航避障雷达SE-0533(CNS-LS05C)激光传感器RS232|RS485|CAN BUS连线说明

AGV小车导航避障雷达SE-0533(CNS-LS05C)激光传感器广泛应用到AGV小车&#xff0c;RGV小车&#xff0c;无人叉车、搬运机器人等领域。本文重点介绍AGV小车导航避障雷达SE-0533(CNS-LS05C)激光传感器RS232|RS485|CAN BUS连线说明。 一、线序定义 1、传感器线缆线序定义 SE-053…

【网络编程】高性能并发服务器源码剖析

hello &#xff01;大家好呀&#xff01; 欢迎大家来到我的网络编程系列之洪水网络攻击&#xff0c;在这篇文章中&#xff0c;你将会学习到在网络编程中如何搭建一个高性能的并发服务器&#xff0c;并且我会给出源码进行剖析&#xff0c;以及手绘UML图来帮助大家来理解&#xf…

【Vue】面试题

vue的组建通信方式 父子关系&#xff1a;props & $emit 、 $parent / $children 、 ref / $refs 、 插槽跨层级关系&#xff1a; provide & inject通用方案&#xff1a;Vuex 或 eventbus 插播&#xff1a;兄弟组建怎么通信&#xff1f; eventbusVuex通过中间件&…

【游戏开发之热更新技术】

游戏开发之热更新技术 热更新技术是指在不重新发布和安装应用的情况下&#xff0c;对已部署的应用程序进行更新和修补的技术。这种技术在现代软件开发中变得越来越重要&#xff0c;因为它能够为用户提供更加及时的服务和更好的体验。以下是一篇关于热更新技术的文章&#xff0…

HttpServletRequest/Response

HttpServletRequest 一些常用类的用法 package Demo;import javax.jws.WebService; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import ja…

【前端】es-drager 图片同比缩放 缩放比 只修改宽 只修改高

【前端】es-drager 图片同比缩放 缩放比 ES Drager 拖拽组件 (vangleer.github.io) 核心代码 //初始宽 let width ref(108)//初始高 let height ref(72)//以下两个变量 用来区分是单独的修改宽 还是高 或者是同比 //缩放开始时的宽 let oldWidth 0 //缩放开始时的高 let o…