Spring Boot 实现 MySQL 数据多选删除功能详解
一、需求与技术选型
1. 核心需求
前端页面支持勾选多条数据,点击 “批量删除” 按钮触发操作;
后端接收选中数据的 ID 集合,验证合法性后批量删除 MySQL 数据;
操作成功 / 失败后返回明确提示,并重载数据列表;
避免 “空删除”(未勾选数据时点击删除)和 “无效 ID 删除”(ID 不存在或格式错误)。
2. 技术栈选型
后端:Spring Boot 2.7.x(简化配置)、Spring MVC(接口开发)、MyBatis-Plus(简化数据库操作,也可替换为原生 MyBatis);
数据库:MySQL 8.0(存储业务数据);
前端:HTML + Vue.js 2.x(数据绑定与交互,也可替换为 React/Angular)、Axios(发送 HTTP 请求)、Element UI(快速搭建表格与勾选组件);
工具:Postman(接口测试)、IDEA(开发 IDE)、Navicat(MySQL 管理)。
二、环境搭建与准备
1. 初始化 Spring Boot 项目
Spring Web:用于开发 HTTP 接口;
MyBatis-Plus Generator:自动生成实体类、Mapper 接口及 XML 文件;
MySQL Driver:MySQL 数据库连接驱动;
Lombok:简化实体类 getter/setter 方法(可选但推荐)。
<!-- Spring Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- MyBatis-Plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version></dependency><!-- MySQL Driver --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
2. 数据库表设计
CREATE TABLE `product` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品ID',`name` varchar(100) NOT NULL COMMENT '商品名称',`price` decimal(10,2) NOT NULL COMMENT '商品价格',`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';-- 插入测试数据INSERT INTO `product` (`name`, `price`) VALUES('手机', 3999.00),('电脑', 5999.00),('平板', 2999.00),('耳机', 799.00);3. 配置文件编写
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8username: root # 你的MySQL用户名password: 123456 # 你的MySQL密码mybatis-plus:mapper-locations: classpath*:mapper/**/*.xml # Mapper XML文件路径type-aliases-package: com.example.demo.entity # 实体类包路径configuration:map-underscore-to-camel-case: true # 开启下划线转驼峰(如create_time -> createTime)
三、后端代码实现(核心步骤)
1. 生成实体类与 Mapper(基于 MyBatis-Plus)
(1)实体类(Product.java)
package com.example.demo.entity;import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.annotation.TableId;import com.baomidou.mybatisplus.annotation.TableName;import lombok.Data;import java.math.BigDecimal;import java.util.Date;@Data@TableName("product") // 对应数据库表名public class Product {@TableId(type = IdType.AUTO) // 自增IDprivate Long id; // 商品IDprivate String name; // 商品名称private BigDecimal price; // 商品价格private Date createTime; // 创建时间private Date updateTime; // 更新时间}(2)Mapper 接口(ProductMapper.java)
package com.example.demo.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.example.demo.entity.Product;import org.apache.ibatis.annotations.Mapper;@Mapper // 标记为MyBatis Mapper接口public interface ProductMapper extends BaseMapper<Product> {// 多选删除可直接使用BaseMapper的deleteBatchIds方法,无需额外定义}2. 服务层(Service)实现
(1)Service 接口(ProductService.java)
package com.example.demo.service;import com.baomidou.mybatisplus.extension.service.IService;import com.example.demo.entity.Product;import com.example.demo.common.Result;import java.util.List;public interface ProductService extends IService<Product> {// 多选删除方法:接收ID列表,返回操作结果Result batchDelete(List<Long> ids);}(2)Service 实现类(ProductServiceImpl.java)
package com.example.demo.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.example.demo.common.Result;import com.example.demo.entity.Product;import com.example.demo.mapper.ProductMapper;import com.example.demo.service.ProductService;import org.springframework.stereotype.Service;import org.springframework.util.CollectionUtils;import java.util.List;@Servicepublic class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {@Overridepublic Result batchDelete(List<Long> ids) {// 1. 验证ID列表:为空则返回错误if (CollectionUtils.isEmpty(ids)) {return Result.error("请选择要删除的数据");}// 2. 优化:校验ID是否存在(避免删除不存在的数据导致误导)long existCount = countByIds(ids);if (existCount != ids.size()) {return Result.error("部分ID不存在,已过滤无效数据,实际可删除" + existCount + "条");}// 3. 调用MyBatis-Plus的批量删除方法(deleteBatchIds)// 底层SQL:DELETE FROM product WHERE id IN (?, ?, ?...)boolean success = removeByIds(ids);// 4. 根据操作结果返回对应信息if (success) {return Result.success("批量删除成功,共删除" + ids.size() + "条数据");} else {return Result.error("批量删除失败,请检查数据是否被占用");}}}注意:MyBatis-Plus 的 removeByIds 方法在删除时,若部分 ID 不存在,仍会返回 true(仅删除存在的 ID)。上述代码通过 countByIds 先校验 ID 存在性,确保结果更精准。
3. 控制层(Controller)编写
package com.example.demo.controller;import com.example.demo.common.Result;import com.example.demo.service.ProductService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController@RequestMapping("/api/product") // 接口统一前缀public class ProductController {@Autowiredprivate ProductService productService;// 多选删除接口:POST请求,接收JSON格式的ID列表@PostMapping("/batch-delete")public Result batchDelete(@RequestBody List<Long> ids) {return productService.batchDelete(ids);}// (可选)查询所有商品接口,用于前端展示列表@PostMapping("/list")public Result list() {return Result.success(productService.list());}}4. 统一结果返回(Result.java)
package com.example.demo.common;import lombok.Data;@Datapublic class Result {// 状态码:200=成功,400=失败,500=服务器异常private Integer code;// 提示信息private String msg;// 返回数据(成功时携带)private Object data;// 成功静态方法(无数据)public static Result success(String msg) {Result result = new Result();result.setCode(200);result.setMsg(msg);return result;}// 成功静态方法(有数据)public static Result success(Object data) {Result result = new Result();result.setCode(200);result.setMsg("操作成功");result.setData(data);return result;}// 失败静态方法public static Result error(String msg) {Result result = new Result();result.setCode(400);result.setMsg(msg);return result;}// 服务器异常静态方法public static Result error500(String msg) {Result result = new Result();result.setCode(500);result.setMsg(msg);return result;}}四、前端页面实现(Vue + Element UI)
1. 页面结构
<template><div class="container"><!-- 标题与删除按钮 --><div class="top-bar"><h2>商品列表</h2><el-buttontype="danger"icon="el-icon-delete"@click="handleBatchDelete":disabled="selectedIds.length === 0" <!-- 未勾选时禁用按钮 -->>批量删除</el-button></div><!-- 商品表格(支持多选) --><el-table:data="productList"borderstripe@selection-change="handleSelectionChange" <!-- 勾选事件:获取选中ID -->style="width: 100%"><!-- 多选框列 --><el-table-column type="selection" width="55"></el-table-column><!-- 商品ID列 --><el-table-column prop="id" label="商品ID" align="center"></el-table-column><!-- 商品名称列 --><el-table-column prop="name" label="商品名称" align="center"></el-table-column><!-- 商品价格列 --><el-table-column prop="price" label="商品价格" align="center"><template slot-scope="scope">{{ scope.row.price.toFixed(2) }}</template></el-table-column><!-- 创建时间列 --><el-table-column prop="createTime" label="创建时间" align="center"><template slot-scope="scope">{{ formatTime(scope.row.createTime) }}</template></el-table-column></el-table></div></template>2. 脚本逻辑
<script>import axios from 'axios'; // 引入Axiosimport { Message, MessageBox } from 'element-ui'; // 引入Element UI组件// 配置Axios基础路径(避免重复写URL前缀)axios.defaults.baseURL = 'http://localhost:8080';export default {name: "ProductList",data() {return {productList: [], // 商品列表数据selectedIds: [] // 选中的商品ID列表};},mounted() {// 页面加载时查询商品列表this.getProductList();},methods: {// 1. 查询所有商品getProductList() {axios.post("/api/product/list").then(res => {if (res.data.code === 200) {this.productList = res.data.data;} else {Message.error(res.data.msg);}}).catch(err => {Message.error("查询失败:" + err.message);});},// 2. 获取选中的商品ID(表格勾选事件)handleSelectionChange(selection) {// 从选中数据中提取ID,存入selectedIdsthis.selectedIds = selection.map(item => item.id);},// 3. 批量删除操作handleBatchDelete() {// 弹出确认框(二次确认,防止误操作)MessageBox.confirm(`确定要删除选中的${this.selectedIds.length}条数据吗?删除后不可恢复!`,'警告',{confirmButtonText: '确定删除',cancelButtonText: '取消',type: 'error'}).then(() => {// 确认后调用后端删除接口axios.post("/api/product/batch-delete", this.selectedIds).then(res => {if (res.data.code === 200) {Message.success(res.data.msg);// 删除成功后重新加载列表this.getProductList();// 清空选中ID(避免下次勾选残留)this.selectedIds = [];} else {Message.error(res.data.msg);}}).catch(err => {Message.error("删除失败:" + err.message);});}).catch(() => {// 取消删除时的提示Message.info('已取消删除操作');});},// 4. 格式化时间(如:2024-05-20 14:30:00)formatTime(time) {if (!time) return '';const date = new Date(time);// 补零函数(处理个位数时间,如5→05)const addZero = (num) => num < 10 ? '0' + num : num;return `${date.getFullYear()}-${addZero(date.getMonth() + 1)}-${addZero(date.getDate())} ${addZero(date.getHours())}:${addZero(date.getMinutes())}:${addZero(date.getSeconds())}`;}}};</script><style scoped>.container {width: 90%;margin: 20px auto;}.top-bar {display: flex;justify-content: space-between;align-items: center;margin-bottom: 20px;}</style>1. 接口测试(Postman)
(1)测试准备
确保本地开发环境已启动,Spring Boot 应用正常运行且端口8080未被占用。可通过访问应用首页或其他已知接口确认服务状态。
若后端接口存在跨域配置,需提前配置允许Postman发起的请求,或临时关闭跨域拦截进行测试。
请求参数:
请求方式:选择POST,因为批量删除操作属于修改数据的写操作,符合 RESTful 规范。
请求体格式:设置为application/json,用于传递需要删除的记录 ID 列表。例如:
{"ids": [1, 3, 5]}其中ids字段为自定义参数名,需与后端 Controller 方法接收的参数名称保持一致,列表元素为待删除数据的主键 ID。
环境变量:
在 Postman 的Environment或Global Environment中,定义变量存储基础 URL(如{{baseUrl}})和端口号(如{{port}}),将请求地址动态拼接为{{baseUrl}}:{{port}}/api/product/batch,便于在不同环境(开发、测试、生产)间快速切换。
若接口需要认证(如 JWT 令牌),需在Authorization选项卡中配置 Bearer Token,或在Headers中添加Authorization: Bearer <token>。
预期结果:
成功响应:状态码应为200 OK,响应体可设计为标准 JSON 格式,例如:
{"code": 200,"message": "批量删除成功","data": null}异常响应:若传入无效参数或删除过程出错,应返回对应状态码(如400 Bad Request表示参数错误,500 Internal Server Error表示服务器异常),并在响应体中包含错误信息。

