多表插入、删除操作(批量)——后端

多表插入

场景:当添加一个菜品时,还需要记录菜品的口味信息,因此需要对菜品表(dish)和口味表(dish_flavor)同时进行插入操作。
两个表的字段:
在这里插入图片描述
代码思路:由DishController将前端的请求派发给相应的业务层(DishService),业务层随后通过调用持久层(DishMapper,DishFlavorMapper)进行数据的增删改。
细节:

  1. 由于要操作两张表,所以需要在业务层的对应方法上添加@Transactional,保证该事务的原子性,见DishServiceImpl;
  2. 插入口味数据时,需要获取刚插入的dish_id值,通过设置useGeneratedKeys实现,见DishMapper.xml;

代码(由上至下):


控制层:
DishController

package com.sky.controller.admin;

import com.sky.dto.DishDTO;
import com.sky.result.Result;
import com.sky.service.DishService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * ClassName: DishController
 * PackageName: com.sky.controller.admin
 * Description: 菜品管理
 *
 * @Author Xiyan Zhong
 * @Create 2023/12/20 下午2:19
 * @Version 1.0
 */

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

}


业务层:

DishService

package com.sky.service;

import com.sky.dto.DishDTO;

/**
 * ClassName: DishService
 * PackageName: com.sky.service
 * Description:
 *
 * @Author Xiyan Zhong
 * @Create 2023/12/20 下午2:23
 * @Version 1.0
 */

public interface DishService {

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

DishServiceImpl:

package com.sky.service.impl;

import com.sky.dto.DishDTO;
import com.sky.entity.Dish;
import com.sky.entity.DishFlavor;
import com.sky.mapper.DishFlavorMapper;
import com.sky.mapper.DishMapper;
import com.sky.service.DishService;
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;

/**
 * ClassName: DishServiceImpl
 * PackageName: com.sky.service.impl
 * Description:
 *
 * @Author Xiyan Zhong
 * @Create 2023/12/20 下午2:25
 * @Version 1.0
 */

@Service
@Slf4j
public class DishServiceImpl implements DishService {

    @Autowired
    private DishMapper dishMapper;

    @Autowired
    private DishFlavorMapper dishFlavorMapper;

    /**
     * 新增菜品和对应的口味
     * @param dishDTO
     */
    @Transactional // 操作多张表的时候,添加@Transactional注解,保证该事件是原子性的,要么全成功,要么全失败
    @Override
    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,为每个口味附上相关的dishId
            flavors.forEach(dishFlavor -> {
                dishFlavor.setDishId(dishId);
            });
            // 向口味表插入n条数据
            dishFlavorMapper.insertBatch(flavors);
        }
    }
}

需要注意的是,为了保证@Transactional有效,需要在当前项目的入口类添加@EnableTransactionManagement,启动注解方式的事务管理,如:

package com.sky;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EnableTransactionManagement //开启注解方式的事务管理
@Slf4j
public class SkyApplication {
    public static void main(String[] args) {
        SpringApplication.run(SkyApplication.class, args);
        log.info("server started");
    }
}

在这里插入图片描述


持久层:

DishMapper 接口

package com.sky.mapper;

import com.sky.annotation.AutoFill;
import com.sky.entity.Dish;
import com.sky.enumeration.OperationType;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

/**
 * ClassName: DishMapper
 * PackageName: com.sky.mapper
 * Description:
 *
 * @Author Xiyan Zhong
 * @Create 2023/12/15 下午2:52
 * @Version 1.0
 */

@Mapper
public interface DishMapper {
    /**
     * 根据分类id查询菜品数量
     * @param categoryId
     * @return
     */
    @Select("select count(id) from dish where category_id = ${categoryId}")
    Integer countByCategoryId(Long categoryId);


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

DishMapper.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">
    <!--设置useGeneratedKeys="true" 表示需要获得插入数据后生成的主键值,返回到keyProperty="id",即Dish对象中的id属性-->
    <insert id="insert" parameterType="Dish" 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>

DishFlavorMapper 接口

package com.sky.mapper;

import com.sky.entity.DishFlavor;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

/**
 * ClassName: DishFlavorMapper
 * PackageName: com.sky.mapper
 * Description:
 *
 * @Author Xiyan Zhong
 * @Create 2023/12/20 下午2:42
 * @Version 1.0
 */
@Mapper
public interface DishFlavorMapper {

    /**
     * 批量插入口味数据
     * @param flavors
     */
    void insertBatch(List<DishFlavor> flavors);
}

DishFlavorMapper.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" parameterType="DishFlavor">
        insert into dish_flavor (dish_id, name, value)
        values
        <foreach collection="flavors" item="df" separator=",">
            (#{df.dishId},#{df.name},#{df.value})
        </foreach>
    </insert>
</mapper>

多表删除

场景:当删除一个菜品时,不仅要删除菜品表中的数据,同时也要删除口味表中对应数据和菜品与分类表中的数据。
相关表的关系:
在这里插入图片描述
代码思路:由DishController将前端的请求派发给相应的业务层(DishService),业务层随后通过调用持久层(DishMapper,DishFlavorMapper,SetmealDishMapper)进行数据的增删改。
细节:

  1. 由于要操作三张表,同样需要在业务层的对应方法上添加@Transactional,保证该事务的原子性,见DishServiceImpl;
  2. 删除菜品数据时,需要完成以下业务操作:
    • 判断当前菜品是否能够删除——是否存在起售中
    • 判断当前菜品是否能够删除——是否被套餐关联
    • 删除菜品表中的菜品数据
    • 删除菜品关联的口味数据

代码(由上至下):


DishController

    @DeleteMapping
    @ApiOperation("菜品删除")
    // 通过@RequestParam解析前端请求地址中字符串参数ids=1,2,3...为一个列表
    public Result delete(@RequestParam List<Long> ids){
        log.info("菜品批量删除:{}",ids);
        dishService.deleteBatch(ids);
        return Result.success();
    }

DishService

	/**
     * 菜品的批量删除
     * @param ids
     */
    void deleteBatch(List<Long> ids);

DishServiceImpl

    /**
     * 菜品的批量删除
     *
     * @param ids
     */
    @Transactional // 保证事务的原子性
    @Override
    public 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);
        }*/

        // sql: delete from dish where id in (?,?,?)
        // 根据菜品id集合批量删除菜品数据
        dishMapper.deleteByIds(ids);

        // 根据菜品id集合批量删除口味数据
        dishFlavorMapper.deleteByDishIds(ids);

    }

SetmealDishMapper提供查询操作
SetmealDishMapper

package com.sky.mapper;

import org.apache.ibatis.annotations.Mapper;

import java.util.List;

/**
 * ClassName: SetmealDishMapper
 * PackageName: com.sky.mapper
 * Description:
 *
 * @Author Xiyan Zhong
 * @Create 2023/12/20 下午7:29
 * @Version 1.0
 */

@Mapper
public interface SetmealDishMapper {

    /**
     * 根据菜品id查询对应的套餐id
     * @param dishIds
     * @return
     */
    List<Long> getSetmealIdsByDishIds(List<Long> dishIds);

}

SetmealDishMapper.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.SetmealDishMapper">
    <!--对应sql语句:select setmeal_id from setmeal_dish where dish_id in (?,?,?)-->
    <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

    /**
     * 根据主键删除菜品数据
     * @param id
     */
    @Delete("delete from dish where id = #{id}")
    void deleteById(Long id);

    /**
     * 根据菜品id集合批量删除菜品
     * @param ids
     */
    void deleteByIds(List<Long> ids);

DishMapper.xml

	<delete id="deleteByIds">
        delete from dish where id in
        <foreach collection="ids" open="(" close=")" separator="," item="id">
            #{id}
        </foreach>
    </delete>

DishFlavorMapper

    /**
     * 根据菜品id删除对应的口味数据
     * @param dishId
     */
    @Delete("delete from dish_flavor where dish_id = #{dishId}")
    void deleteByDishId(Long dishId);

    /**
     * 据菜品id批量删除对应的口味数据
     * @param dishIds
     */
    void deleteByDishIds(List<Long> dishIds);

DishFlavorMapper.xml

	<delete id="deleteByDishIds">
        delete from dish_flavor where dish_id in
        <foreach collection="dishIds" open="(" close=")" separator="," item="dishId">
            #{dishId}
        </foreach>
    </delete>

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

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

相关文章

市场全局复盘 20231220

短线核心&#xff1a;不参与任何级别的调整 昨日回顾&#xff1a; SELECT CODE,成交额排名,净流入排名,代码,名称,DDE大单金额,涨幅,主力净额,DDE大单净量,CONVERT(DATETIME, 最后封板, 120) AS 最后封板,涨停分析,_3日涨幅百分比,连板天,封单额,封单额排名,DDE散户数量,总金额…

Android Studio使用Genymotion

1. Genymotion介绍 GenyMotion速度之快令人发指&#xff0c;模拟效果堪比真机调试&#xff0c;支持绝大部分的模拟器功能&#xff0c;甚至包括语音&#xff0c;Google Now&#xff0c;支持eclipse, android studio。非常适合用来开发和演示效果。 2. Genymotion下载 Genymotio…

CentOS操作学习(二)

上一篇学习了CentOS的常用指令CentOS指令学习-CSDN博客 现在我们接着学习 一、Vi编辑器 这是CentOS中自带的编辑器 三种模式 进入编辑模式后 i&#xff1a;在光标所在字符前开始插入a&#xff1a;在光标所在字符串后开始插入o&#xff1a;在光标所在行的下面另起一新行插入…

Java操作Word修订功能:启用、接受、拒绝、获取修订

Word的修订功能是一种在文档中进行编辑和审阅的功能。它允许多个用户对同一文档进行修改并跟踪这些修改&#xff0c;以便进行审查和接受或拒绝修改。修订功能通常用于团队合作、专业编辑和文件审查等场景。 本文将从以下几个方面介绍如何使用免费工具Free Spire.Doc for Java在…

使用包、Crate 和模块管理项目(下)

1、使用 use 关键字将路径引入作用域 在之前的示例中我们引用模块中的函数或者结构体之类的&#xff0c;都是需要用到相对路径或者绝对路径去引用&#xff0c;然尔在这里&#xff0c;有一种方法可以简化这个过程。我们可以使用 use 关键字创建一个短路径&#xff0c;然后就可以…

创建Maven Web工程

目录下也会有对应的生命周期。其中常用的是&#xff1a;clean、compile、package、install。 比如这里install &#xff0c;如果其他项目需要将这里的模块作为依赖使用&#xff0c;那就可以 install 。安装到本地仓库的位置&#xff1a; Java的Web工程&#xff0c;所以我们要选…

Ubuntu上安装MySQL以及hive

Ubuntu上安装MySQL以及hive 一、安装MySQL1、更新软件源2、安装 MySQL3、启动 MySQL&#xff0c;并登录 MySQL4、关闭 MySQL 指令&#xff1a;5、修改登录密码6、关闭 mysql&#xff0c;然后重新进入 二、安装hive1、创建 hive 的数据库2、下载压缩包3、修改环境配置文件并激活…

【ECharts】折线图

文章目录 折线图1折线图2折线图3示例 参考&#xff1a; Echarts官网 Echarts 配置项 折线图1 带X轴、Y轴标记线&#xff0c;其中X轴是’category’ 类目轴&#xff0c;适用于离散的类目数据。 let myChart echarts.init(this.$refs.line_chart2); let yList [400, 500, 6…

使用postman时,报错SSL Error: Unable to verify the first certificate

开发中使用postman调用接口&#xff0c;出现以下问题&#xff0c;在确认路径、参数、请求方式均为正确的情况下 解决方法 File - Settings -> SSL certification verification 关闭 找到图中配置&#xff0c;这里默认是打开状态&#xff0c;把它关闭即可&#xff1a;ON …

智能化制造与工业自动化:发展历程、问题与解决、未来趋势及全球应用

导言 智能化制造与工业自动化正成为全球制造业的主要趋势。本文将深入研究其发展历程、遇到的问题及解决过程、未来的可用范围&#xff0c;以及在各国的应用和未来的研究趋势。同时&#xff0c;将讨论在哪些方面能够取得胜利&#xff0c;并在哪些方面发力&#xff0c;实现自身价…

JavaWeb笔记之前端开发HTML

一、引言 1.1HTML概念 网页&#xff0c;是网站中的一个页面&#xff0c;通常是网页是构成网站的基本元素&#xff0c;是承载各种网站应用的平台。通俗的说&#xff0c;网站就是由网页组成的。通常我们看到的网页都是以htm或html后缀结尾的文件&#xff0c;俗称 HTML文件。 …

Docker 网络模式 -day05

docker 启动时候还会有&#xff0c;名为docker0的虚拟网桥&#xff0c;注意网址为 127.0.0.1 [rootiZuf6hxabqikytnrumsi4gZ ~]# ifconfig docker0: flags4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.2…

ChatGPT如何计算token数?

GPT 不是适用于某一门语言的大型语言模型&#xff0c;它适用于几乎所有流行的自然语言。所以 GPT 的 token 需要 兼容 几乎人类的所有自然语言&#xff0c;那意味着 GPT 有一个非常全的 token 词汇表&#xff0c;它能表达出所有人类的自然语言。如何实现这个目的呢&#xff1f;…

RK3568平台开发系列讲解(Linux系统篇)GPIO接口介绍

🚀返回专栏总目录 文章目录 一、GPIO 子系统接口二、GPIO描述符相关结构体沉淀、分享、成长,让自己和他人都能有所收获!😄 📢在目前的 Linux 内核主线中,GPIO(通用输入/输出)子系统存在两个版本,这里将两个版本区分为新版本和旧版本。新版本 GPIO 子系统接口是基于…

【项目管理】redmine

Redmine是用Ruby开发的基于web的项目管理软件&#xff0c;是用ROR框架开发的一套跨平台项目管理系统&#xff0c;据说是源于Basecamp的ror版而来&#xff0c;支持多种数据库&#xff0c;有不少自己独特的功能&#xff0c;例如提供wiki、新闻台等&#xff0c;还可以集成其他版本…

nodejs微信小程序+python+PHP购物商城网站-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

防反接电路与MOS管防反接深入解析

一、经典防反接电路 1、二极管防反接 这种电路使用一个二极管将电源的正极与负极相连&#xff0c;当电源的极性正确时&#xff0c;二极管处于正向导通状态&#xff0c;电流可以正常流过&#xff1b;而当电源的极性反接时&#xff0c;二极管处于反向截止状态&#xff0c;电流无…

函数(C++)

1.7 函数1.7.1 函数缺省参数1.7.2 哑元1.7.3 引用参数1.7.4 返回引用1.7.5 函数重载 1.7 函数 1.7.1 函数缺省参数 在C中&#xff0c;函数的形参列表中的形参是可以有默认值的。有默认值的参数即为默认参数。 在函数调用时&#xff0c;有默认参数可以缺省。 语法&#xff1…

关于Python里xlwings库对Excel表格的操作(十五)

这篇小笔记主要记录如何【获取单元格数据的对齐方式或更改单元格数据的对齐方式】。 前面的小笔记已整理成目录&#xff0c;可点链接去目录寻找所需更方便。 【目录部分内容如下】【点击此处可进入目录】 &#xff08;1&#xff09;如何安装导入xlwings库&#xff1b; &#xf…

你想改win11系统中窗口、菜单等的字体? 微软不想让你改

如果你感觉win11系统中显示的字体不好看&#xff0c;想换。等一等&#xff0c;微软本不想让你改。 Windows 11 在默认情况下并没有提供直接修改系统默认字体的选项&#xff0c;而需要进行注册表或其他高级设置来更改系统字体。这可能是因为微软希望保持系统的一致性和稳定性&a…