乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 MyBatis概述与快速上手

    • 本章定位
    • MyBatis简介
    • 环境搭建
    • 第一个MyBatis程序
    • SqlSessionFactoryBuilder与openSession重载
    • SqlSessionFactory与SqlSession
    • SqlSession核心方法
    • 不使用 XML 构建 SqlSessionFactory
    • Mapper接口与映射方式
    • Java API 目录结构
  • 第2章 全局配置文件详解

    • 本章定位
    • properties
    • settings
    • typeAliases
    • typeHandlers
    • objectFactory
    • plugins
    • environments
    • transactionManager
    • dataSource
    • databaseIdProvider
    • mappers
    • 日志配置
  • 第3章 SQL映射文件基础

    • 本章定位
    • select
    • insert
    • update
    • delete
    • 参数传递与占位符
    • 主键生成策略
    • resultType
    • resultMap
    • 自动映射详解
    • sql片段
    • SQL 语句构建器
  • 第4章 动态SQL

    • 本章定位
    • if
    • choose、when、otherwise
    • where
    • set
    • foreach
    • trim
    • bind
    • script 元素:在注解映射器中启用动态 SQL
    • _databaseId 与动态 SQL 的多数据库支持
    • 动态 SQL 中插入脚本语言
  • 第5章 结果映射与关联查询

    • 本章定位
    • resultMap详解
    • association
    • collection
    • discriminator
    • N+1查询问题
    • 延迟加载
  • 第6章 MyBatis注解开发

    • 本章定位
    • @Select
    • @Insert
    • @Update
    • @Delete
    • @Param
    • @Options
    • @SelectKey
    • @Results
    • @Result
    • @One
    • @Many
    • @SelectProvider
  • 第7章 缓存与性能优化

    • 本章定位
    • 一级缓存
    • 二级缓存
    • 缓存配置详解
    • 自定义缓存
    • Executor执行器类型
    • 分页插件

delete

导学

本节将掌握MyBatis中delete映射元素的配置与使用。你将学会如何编写删除语句,理解按ID删除与批量删除的写法差异,掌握删除返回影响行数的含义,并了解为第04章foreach做铺垫的集合参数传递方式。

定义

delete是SQL映射文件中用于定义删除语句的元素。在JDBC原始写法中,删除数据需要手动编写DELETE语句、设置WHERE条件、执行executeUpdate()并获取影响行数。若需批量删除,还要拼接IN子句或循环执行单条删除。delete元素将这一切声明化:写好SQL模板后,MyBatis自动完成参数绑定与执行,并支持通过集合参数实现批量删除的优雅写法。

适用位置与核心属性

delete元素书写在映射文件的<mapper>根标签内部,与select、insert、update并列。

属性是否必填说明
id是映射语句的唯一标识,与Mapper接口方法名对应
parameterType否传入参数的全限定类名或别名,MyBatis可自动推断
flushCache否执行后是否清空本地缓存与二级缓存,默认true
timeout否驱动等待数据库返回的最大秒数,默认未设置

delete的属性与update高度一致,因为二者都属于DML写操作,核心差异仅在于SQL语义。

核心原理

MyBatis执行delete语句时,通过PreparedStatement完成预编译删除,并将数据库返回的影响行数透传给调用方。批量删除时,MyBatis会将集合参数展开为IN子句或多次执行。

  1. Java调用:开发者调用StudentMapper.deleteById(5)或StudentMapper.deleteByIds(List)。
  2. SqlSession & Executor:会话层调度执行器,准备执行删除。
  3. StatementHandler:创建PreparedStatement,绑定#{id}或展开集合参数。
  4. JDBC执行:向MySQL 5.7发送DELETE语句,数据库完成数据删除。
  5. 返回影响行数:MySQL返回实际被删除的行数。
  6. 清空缓存:flushCache默认true,删除后清空缓存,防止后续查询读到已删除的幽灵数据。

完整示例

场景说明

乐途公司技术部对学员系统进行年度清理:李眉(id=5)已毕业离校,需要单独删除其记录;同时,一批测试账号(id=6,7,8)也需要批量清理。本节演示单条删除与批量删除两种场景。

操作前的数据库表结构及初始数据

CREATE TABLE student (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(20),
    age INT,
    major VARCHAR(20),
    score DECIMAL(5,2)
);

INSERT INTO student (name, age, major, score) VALUES
('大翔', 22, '计算机科学', 95.5),
('白歌', 21, '软件工程', 88.0),
('小崔', 20, '计算机科学', 92.0),
('黄俪', 21, '信息安全', 90.5),
('李眉', 22, '软件工程', 87.0),
('测试账号A', 18, '测试专业', 0.0),
('测试账号B', 18, '测试专业', 0.0),
('测试账号C', 18, '测试专业', 0.0);

当前数据状态:

idnameagemajorscore
1大翔22计算机科学95.50
2白歌21软件工程88.00
3小崔20计算机科学92.00
4黄俪21信息安全90.50
5李眉22软件工程87.00
6测试账号A18测试专业0.00
7测试账号B18测试专业0.00
8测试账号C18测试专业0.00

完整的映射文件片段与Java代码

Mapper接口

package com.flywing.mapper;

import org.apache.ibatis.annotations.Param;
import java.util.List;

public interface StudentMapper {
    // 按ID删除单条记录
    int deleteById(Integer id);

    // 批量删除(为第04章foreach做铺垫,本章展示基础写法)
    int deleteByIds(@Param("ids") List<Integer> ids);
}

映射文件 StudentMapper.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.flywing.mapper.StudentMapper">

    <!-- 场景一:按ID删除单条记录 -->
    <delete id="deleteById" parameterType="java.lang.Integer">
        DELETE FROM student WHERE id = #{id}
    </delete>

    <!--
        场景二:批量删除
        本章使用基础IN子句写法,第04章将讲解foreach标签的动态展开
    -->
    <delete id="deleteByIds">
        DELETE FROM student WHERE id IN
        <!-- 此处先硬编码演示,实际生产环境应使用foreach动态生成 -->
        (6, 7, 8)
    </delete>

</mapper>

注:上述deleteByIds中硬编码的(6,7,8)仅用于本章演示delete元素本身。真正的动态批量删除将在第04章通过<foreach>标签实现,如:

<delete id="deleteByIds">
    DELETE FROM student WHERE id IN
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</delete>

测试代码

package com.flywing.test;

import com.flywing.mapper.StudentMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

public class DeleteTest {
    public static void main(String[] args) throws Exception {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = factory.openSession();
        StudentMapper mapper = session.getMapper(StudentMapper.class);

        // 场景一:删除李眉(id=5)
        int rows1 = mapper.deleteById(5);
        System.out.println("删除李眉,影响行数:" + rows1);

        // 场景二:批量删除测试账号(id=6,7,8)
        // 本章先演示基础delete元素,集合参数传递为第04章铺垫
        List<Integer> testIds = Arrays.asList(6, 7, 8);
        int rows2 = mapper.deleteByIds(testIds);
        System.out.println("批量删除测试账号,影响行数:" + rows2);

        session.commit();
        session.close();
    }
}

实际执行结果

控制台SQL输出

[DEBUG] com.flywing.mapper.StudentMapper.deleteById - ==>  Preparing: DELETE FROM student WHERE id = ?
[DEBUG] com.flywing.mapper.StudentMapper.deleteById - ==> Parameters: 5(Integer)
[DEBUG] com.flywing.mapper.StudentMapper.deleteById - <==    Updates: 1
删除李眉,影响行数:1

[DEBUG] com.flywing.mapper.StudentMapper.deleteByIds - ==>  Preparing: DELETE FROM student WHERE id IN (6, 7, 8)
[DEBUG] com.flywing.mapper.StudentMapper.deleteByIds - ==> Parameters:
[DEBUG] com.flywing.mapper.StudentMapper.deleteByIds - <==    Updates: 3
批量删除测试账号,影响行数:3

删除后的数据库状态

idnameagemajorscore
1大翔22计算机科学95.50
2白歌21软件工程88.00
3小崔20计算机科学92.00
4黄俪21信息安全90.50

分析

  1. 单条删除:deleteById使用#{id}预编译绑定,安全且高效。返回值为1表示成功删除一条记录;若传入不存在的ID,返回值为0。
  2. 批量删除的过渡写法:本章的deleteByIds为了聚焦delete元素本身,采用了硬编码IN子句。这种方式在ID列表固定时可用,但缺乏灵活性。第04章将引入<foreach>标签,实现IN子句的动态生成,使批量删除真正具备实用价值。
  3. 事务提交:删除操作同样需要session.commit()。未提交时,虽然executeUpdate()返回了影响行数,但数据并未真正从数据库移除。

易错场景/常见误区

误区正解
删除时不写WHERE条件缺少WHERE的DELETE会清空全表,是生产环境最危险的操作之一;务必再三确认条件存在
认为delete返回被删除记录的主键返回值是影响行数,不是主键;若需知道删除了谁,应先SELECT再DELETE
批量删除时手动拼接SQL字符串应使用MyBatis的<foreach>标签(第04章)或#{}占位符,避免字符串拼接带来的SQL注入风险
删除后不commit与insert、update相同,delete也需要显式提交事务
用delete做逻辑删除逻辑删除(标记记录为无效而非物理删除)应使用update修改状态字段,而非delete;物理删除不可逆

面试考点

Q1:MyBatis的delete返回什么?

返回int,表示实际被数据库删除的行数。若WHERE条件未命中任何记录,返回0。该值来自JDBC的Statement.getUpdateCount(),可用于判断删除是否成功命中目标。

Q2:批量删除应该如何实现?

推荐在delete元素中使用<foreach>标签动态生成IN子句,如DELETE FROM student WHERE id IN <foreach collection="ids" item="id" separator=",">#{id}</foreach>。这种方式既安全(使用#{}预编译绑定)又灵活(支持任意长度的集合)。第04章将详细讲解foreach的用法。

Q3:delete的flushCache默认是什么?

默认true。删除操作会改变数据库状态,清空缓存可防止后续查询读到已删除的数据。与update和insert保持一致,三者都属于写操作,默认都会使缓存失效。

Q4:物理删除和逻辑删除有什么区别?MyBatis中分别如何实现?

物理删除是将记录从数据库中彻底移除,使用delete元素;逻辑删除是更新一个状态字段(如is_deleted=1),使用update元素。物理删除不可逆且可能影响关联数据,逻辑删除保留数据便于审计和恢复。实际业务中逻辑删除更为常见。

小结

delete元素负责数据移除的声明化映射。单条删除通过#{id}精确命中,批量删除通过IN子句扩展。与insert、update一样,delete的返回值是实际影响行数,且默认会清空缓存。本章展示了delete的基础写法,真正的动态批量删除将在第04章通过<foreach>标签完善。

下一章引子

增删改查的SQL语句写好后,查询结果如何变成Java对象?当数据库列名与实体属性名完全一致时,MyBatis可以自动完成映射;但一旦命名风格不一致(如数据库用stu_name,Java用stuName),自动映射就会失效。下一节将深入讲解resultType的适用边界、自动映射原理,以及列名不一致时暴露的问题。

上一页
update
下一页
参数传递与占位符