【Spring Boot 快速入门】四、MyBatis

发布于:2025-08-01 ⋅ 阅读:(27) ⋅ 点赞:(0)

MyBatis(一)入门

简介

MyBatis 是一款 优秀的持久层框架,它支持 自定义 SQL、存储过程以及高级映射,是 Java 开发中连接数据库的常用工具之一,属于 ORM(对象关系映射)框架 的一种实现形式。

MyBatis 最初是由 Apache 团队开发的 iBatis 项目,后来由 Google Code 迁移到 GitHub 并更名为 MyBatis。它是一个半自动化的 ORM 框架,开发者自己写 SQL,MyBatis 负责将 SQL 的执行结果与 Java 对象进行自动映射。

MyBatis 的核心特点:

  • SQL 编写自由:开发者可以完全控制 SQL,实现灵活的数据库操作
  • 简单易用:学习成本低、配置清晰
  • 支持映射关系:支持一对一、一对多等对象映射
  • 动态 SQL:支持 if、choose、where 等标签,动态拼接 SQL
  • 与 Spring 整合:配合 Spring Boot 使用非常方便
  • 缓存支持:内置一级缓存,支持二级缓存插件扩展

MyBatis 工作原理:

  1. Java 调用 Mapper 接口
  2. MyBatis 根据配置 XML/注解
  3. 执行 SQL
  4. 映射结果
  5. 返回 Java 对象

MyBatis 入门

步骤:

  1. 准备工作(创建工程、数据库表、实体类)
  2. 引入 MyBatis 相关依赖,配置 MyBatis
  3. 编写 SQL 语句(注解/XML)

创建工程除了添加 Spring Web 依赖,还要添加 MyBatis Framework 和 MySQL Driver 依赖:

在这里插入图片描述

连接数据源,选择 MySQL:

在这里插入图片描述

填写用户名、密码和要连接的数据库名,点击测试连接,成功即可应用:

在这里插入图片描述

在配置文件 application.properties 中配置数据库信息:

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
spring.datasource.username=root
spring.datasource.password=123456

创建数据库及表:

create database if not exists `mybatis`;
use `mybatis`;
create table if not exists user (
    id int primary key auto_increment,
    name varchar(20),
    age tinyint,
    gender tinyint comment '1-male, 2-female',
    phone varchar(20)
)comment '用户表';
insert into user (name, age, gender, phone) values
        ('赵刚', 18, 1, '12345678901'),
        ('王芳', 19, 2, '12345678902'),
        ('林伟', 20, 1, '12345678903'),
        ('马丽', 21, 2, '12345678904'),
        ('孙浩', 22, 1, '12345678905');

对应的实体类:

public class User {
    private Integer id;
    private String name;
    private Short age;
    private Short sex;
    private String phone;

    public User() {}

    public User(Integer id, String name, Short age, Short sex, String phone) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.phone = phone;
    }

    public Integer getId() {return id;}

    public void setId(Integer id) {this.id = id;}

    public String getName() {return name;}

    public void setName(String name) {this.name = name;}

    public Short getAge() {return age;}

    public void setAge(Short age) {this.age = age;}

    public Short getSex() {return sex;}

    public void setSex(Short sex) {this.sex = sex;}

    public String getPhone() {return phone;}

    public void setPhone(String phone) {this.phone = phone;}

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                ", phone='" + phone + '\'' +
                '}';
    }
}

创建 mapper 接口(原来的 dao 层):

import com.example.demo.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;

@Mapper //在运行时会自动生成该接口的实现类对象(代理对象),并且将该对象交给Spring的IOC容器管理
public interface UserMapper {
    @Select("select * from user")
    public List<User> list();
}

在测试类中编写测试代码并运行:

import com.example.demo.mapper.UserMapper;
import com.example.demo.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;

@SpringBootTest
class Demo2ApplicationTests {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void testListUser() {
        List<User> userList = userMapper.list();
        for (User user : userList) {
            System.out.println(user);
        }
    }
}

控制台显示:

在这里插入图片描述

Lombok

Lombok 是一个实用的 Java 类库,能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString 等方法,并可以自动化生成日志变量,简化 Java 开发、提高效率。

注解 作用
@Getter/@Setter 为所有的属性提供 get/set 方法
@ToString 会给类自动生成易阅读的 toString 方法
@EqualsAndHashCode 根据类所拥有的非静态字段自动重写 equals 方法和 hashCode 方法
@Data 提供了更综合的生成代码功能(@Getter + @Setter + @ToString + @EqualsAndHashCode)
@NoArgsConstructor 为实体类生成无参的构造器方法
@AllArgsConstructor 为实体类生成除了 static 修饰的字段之外带有各参数的构造器方法。

Lombok 依赖:

<dependency>
	<groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
</dependency>

在一些 IDEA 老的版本中没有集成 Lombok 插件,需要自行前往插件市场安装应用。

MyBatis 基础操作

数据准备

创建数据库表:

-- 部门管理
create table dept(
    id int unsigned primary key auto_increment comment '主键ID',
    name varchar(10) not null unique comment '部门名称',
    create_time datetime not null comment '创建时间',
    update_time datetime not null comment '修改时间'
) comment '部门表';

insert into dept (id, name, create_time, update_time) values
    (1,'学工部',now(),now()),
    (2,'教研部',now(),now()),
    (3,'咨询部',now(),now()),
    (4,'就业部',now(),now()),
    (5,'人事部',now(),now());

-- 员工管理
create table emp (
    id int unsigned primary key auto_increment comment 'ID',
    username varchar(20) not null unique comment '用户名',
    password varchar(32) default '123456' comment '密码',
    name varchar(10) not null comment '姓名',
    gender tinyint unsigned not null comment '性别, 说明: 1 男, 2 女',
    job tinyint unsigned comment '职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师',
    entrydate date comment '入职时间',
    dept_id int unsigned comment '部门ID',
    create_time datetime not null comment '创建时间',
    update_time datetime not null comment '修改时间'
) comment '员工表';

INSERT INTO emp(id, username, password, name, gender, job, entrydate,dept_id, create_time, update_time) VALUES
    (1,'zhangwei','123456','张伟',1,4,'2000-01-01',2,now(),now()),
    (2,'liqiang','123456','李强',1,2,'2015-01-01',2,now(),now()),
    (3,'wangjun','123456','王军',1,2,'2008-05-01',2,now(),now()),
    (4,'liuyang','123456','刘洋',1,2,'2007-01-01',2,now(),now()),
    (5,'chenming','123456','陈明',1,2,'2012-12-05',2,now(),now()),
    (6,'humin','123456','胡敏',2,3,'2013-09-05',1,now(),now()),
    (7,'zhuyan','123456','朱妍',2,1,'2005-08-01',1,now(),now()),
    (8,'guoyan','123456','郭燕',2,1,'2014-11-09',1,now(),now()),
    (9,'linling','123456','林玲',2,1,'2011-03-11',1,now(),now()),
    (10,'heqian','123456','何倩',2,1,'2013-09-05',1,now(),now()),
    (11,'gaoxiang','123456','高翔',1,5,'2007-02-01',3,now(),now()),
    (12,'liangchao','123456','梁超',1,5,'2008-08-18',3,now(),now()),
    (13,'luoyi','123456','罗毅',1,5,'2012-11-01',3,now(),now()),
    (14,'mahui','123456','马辉',1,2,'2002-08-01',2,now(),now()),
    (15,'huangyong','123456','黄勇',1,2,'2011-05-01',2,now(),now()),
    (16,'wupeng','123456','吴鹏',1,2,'2010-01-01',2,now(),now()),
    (17,'zhenlei','123456','郑磊',1,NULL,'2015-03-21',NULL,now(),now());

创建实体类:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDate;
import java.time.LocalDateTime;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {
    private Integer id;
    private String username;
    private String password;
    private String name;
    private Short gender;
    private Short job;
    private LocalDate entrydate;
    private Integer deptid;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

mapper 接口:

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface EmpMapper {
  
}

后面的操作都是按照以上数据进行

删除

在 mapper 接口中编写删除操作的代码:

//根据ID删除数据
@Delete("delete from emp where id=#{id}") // #{} 是 MyBatis 中动态获取数据的占位符
public int deleteById(Integer id);

在测试类中编写测试方法的代码:

@Autowired
private EmpMapper empMapper;
@Test
public void testDelete(){
	empMapper.deleteById(17);
}

一般这样写是没有返回值,如果需要看是否删除了数据,可以写成以下形式;

@Test
public void testDelete(){
	int deleteNum =empMapper.deleteById(17);
	System.out.println("删除了"+deleteNum+"行数据");
}

运行结果如下:
在这里插入图片描述

预编译

虽然前面的操作成功执行了,但是我们无法知道底层到底是怎么进行的,这个时候可以通过配置 MyBatis 日志来了解

在配置文件 application.properties 中加入以下配置即可开启 MyBatis 日志,并输出到控制台中:

mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

运行测试方法就可以在控制台得到以下信息:

在这里插入图片描述

这样我们就能了解到底层是怎么进行的了

==>  Preparing: delete from emp where id=?
==> Parameters: 17(Integer)

这是 SQL 中的预编译(Prepared Statement)是数据库编程中一种常见的优化和防注入方式。它将 SQL 语句的结构与数据参数分开处理,大大提高了执行效率并增强了安全性。

SQL 预编译是将 SQL 语句在数据库执行前先进行编译,生成执行计划,在实际执行时只需提供参数即可。预编译过程主要包括:

  1. 解析 SQL 结构
  2. 检查语法和语义
  3. 生成执行计划
  4. 缓存该语句(可重复使用)

使用 #{} 的方式,MyBatis 会生成使用 ? 占位符的预编译 SQL,数据库只需预编译一次并可复用执行计划,后续的不同参数只需要替换 ? 即可;而写死参数的 SQL,每次都是新的语句,数据库必须重新预编译,效率低。

预编译的优势:

  1. 性能优化:SQL 结构只编译一次,多次执行复用执行计划,效率更高
  2. 防 SQL 注入:参数绑定,不会直接拼接SQL字符串,防止恶意注入
  3. 代码更简洁:统一结构 + 参数替换,代码更清晰

那么什么又是 SQL 注入呢?用以下场景来演示:

  • 用户登录功能就是在数据库表中查找是否有对应的用户名和密码

    //根据用户名和密码查询用户
    @Select("select * from emp where username='zhangwei' and password='123456'")
    public Emp getEmpByUsernameAndPassword();
    

    结果如下,登录成功:

    在这里插入图片描述

  • 而现在用户名随便输入,密码输入“'or'1'='1”:

    //根据用户名和密码查询用户
    @Select("select count(*) from emp where username='asfgasgasf' and password=''or'1'='1'")
    public int getEmpByUsernameAndPassword();
    

    结果也是登录成功:

    在这里插入图片描述

这种情况就称为 SQL 注入,出现这种情况的原因是:

  • 在 mapper 接口中写的 SQL 语句 @Select("select count(*) from emp where username='asfgasgasf' and password=''or'1'='1'") 在数据库中解析为 SELECT count(*) FROM emp WHERE username='asfgasgasf' AND password='' OR '1'='1',因为 '1'='1' 永远为真,整个 WHERE 条件恒为真,导致查询返回整张表的总行数。因此,如果后台通过 count > 0 判断用户是否存在,就会错误地认为登录验证通过,从而实现 SQL 注入攻击。

而用了预处理的代码如下:

//根据用户名和密码查询用户
@Select("select count(*) from emp where username=#{username} and password=#{password}")
public int getEmpByUsernameAndPassword(String username,String password);

@Test
public void testGetEmpByUsernameAndPassword(){
	int count = empMapper.getEmpByUsernameAndPassword("zhangsan","'or'1'='1");
	System.out.println(count);
}

运行测试结果如下:

在这里插入图片描述

显而易见,'or'1'='1 是以一个整体来替换 ?,不会当作 SQL 语法解析,也就无法注入了

参数占位符;

语法格式 特点及说明 使用时机
#{...} 执行 SQL 时,会将#{...}替换为?,生成预编译 SQL,会自动设置参数值,可有效防止 SQL 注入 参数传递场景,一般参数传递都使用#{...}
${...} 拼接 SQL,直接将参数拼接到 SQL 语句中,存在 SQL 注入问题 对表名、列名进行动态设置等场景(需谨慎,做好校验避免注入风险 )

新增

mapper 接口的代码:

//新增员工
@Insert("insert into emp(username,password,name,gender,job,entrydate,dept_id,create_time,update_time) " +
            "values(#{username},#{password},#{name},#{gender},#{job},#{entryDate},#{deptId},#{createTime},#{updateTime})")
public void insert(Emp emp);

测试类中的代码:

@Test
public void testInsert(){
	Emp emp = new Emp();
	emp.setUsername("张三");
	emp.setPassword("123456");
	emp.setName("张三");
	emp.setGender((short) 1);
	emp.setJob((short) 1);
	emp.setEntryDate(LocalDate.now());
	emp.setDeptId(1);
	emp.setCreateTime(LocalDateTime.now());
	emp.setUpdateTime(LocalDateTime.now());

  empMapper.insert(emp);
}

运行结果如下:

在这里插入图片描述

主键返回:在数据添加成功后,需要获取插入数据库数据的主键。如:添加套餐数据时,还需要维护套餐菜品关系表数据

只需要加上 @Options 注解即可:

//新增员工
@Options(useGeneratedKeys = true,keyProperty = "id")
@Insert("insert into emp(username,password,name,gender,job,entrydate,dept_id,create_time,update_time) " +
            "values(#{username},#{password},#{name},#{gender},#{job},#{entryDate},#{deptId},#{createTime},#{updateTime})")
public void insert(Emp emp);

运行结果如下:

在这里插入图片描述

更新

mapper 接口的代码:

//更新员工
@Update("update emp set username=#{username},password=#{password},name=#{name},gender=#{gender},job=#{job}, entrydate=#{entryDate},dept_id=#{deptId},update_time=#{updateTime} where id=#{id}")
public void update(Emp emp);

测试类中的代码:

@Test
public void testUpdate(){
	Emp emp = new Emp();
	emp.setId(19);
	emp.setUsername("lisi(update)");
	emp.setPassword("123456");
	emp.setName("李四(update)");
	emp.setGender((short) 1);
	emp.setJob((short) 2);
	emp.setEntryDate(LocalDate.now());
	emp.setDeptId(1);
	emp.setCreateTime(LocalDateTime.now());
	emp.setUpdateTime(LocalDateTime.now());
  
	empMapper.update(emp);
}

运行结果如下:

在这里插入图片描述

查询

mapper 接口的代码:

//根据ID查询员工
@Select("select * from emp where id=#{id}")
public Emp getById(Integer id);

测试类中的代码:

@Test
public void testGetById(){
	Emp emp = empMapper.getById(1);
	System.out.println(emp);
}

运行结果如下:

在这里插入图片描述

从结果中会发现,最后三个字段在数据库表中明明是有数据,却没有被获取到,这是因为 MyBatis 数据封装的原因。

数据封装:

  • 实体类属性名和数据库表查询返回的字段名一致,MyBatis 会自动封装。
  • 如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装。

解决方法:

  1. 给字段起别名:

    //根据ID查询员工
    @Select("select id, username, password, name, gender, job, entrydate, dept_id deptId, create_time createTime, update_time updateTime from emp where id=#{id}")
    public Emp getById(Integer id);
    
  2. 通过 @Results,@Result 注解手动映射封装

    //根据ID查询员工
    @Results({
            @Result(column = "dept_id", property = "deptId"),
            @Result(column = "create_time", property = "createTime"),
            @Result(column = "update_time", property = "updateTime")
    })
    @Select("select * from emp where id=#{id}")
    public Emp getById(Integer id);
    
  3. 开启 MyBatis 的驼峰命名自动映射开关(推荐,但是类中的属性名必须要是驼峰命名,数据库表字段名必须要是 _ 命名)

    //application.properties
    mybatis.configuration.map-underscore-to-camel-case=true
    

    运行结果如下:

    在这里插入图片描述

以上根据 ID 查询较为简单,而下面的条件查询则较为复杂。

mapper 接口的代码:

//条件查询员工
@Select("select * from emp where name like '%${name}%' and gender=#{gender} and entrydate between #{begin} and #{end} order by update_time desc")
public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);

测试类中的代码:

@Test
public void testList(){
	List<Emp> list = empMapper.list("张", (short) 1, LocalDate.of(2000, 1, 1), LocalDate.now());
	System.out.println(list);
}

运行结果如下:

在这里插入图片描述

但是接口代码中使用的是 ${},存在 SQL 注入的问题,可以使用 SQL 中的 concat 函数来进行拼接:

//条件查询员工
@Select("select * from emp where name like concat('%',#{name},'%') and gender=#{gender} and entrydate between #{begin} and #{end} order by update_time desc")
public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);

这样就能使用 #{} 来解决 SQL 注入的问题。

XML 映射文件

XML 映射文件规范:

  • XML 映射文件的名称与 mapper 接口名称一致,并且将 XML 映射文件和 mapper 接口放置在相同包下(同包同名)
  • XML 映射文件的 namespace 属性为 mapper 接口全限定名一致
  • XML 映射文件中 SQL 语句的 id 与 mapper 接口中的方法名一致,并保持返回类型一致

在 resources 包下创建 mapper 接口的同名包:

在这里插入图片描述

在新创建的包下创建 mapper 接口的同名 XML 文件 EmpMapper.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 接口中的方法为:

public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);

EmpMapper.xml 中写 SQL 语句:

<?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.example.demo.mapper.EmpMapper">
    <select id="list" resultType="com.example.demo.pojo.Emp">
    <!--resultType 是单条记录封装的类型,要用对应实体类的全类名-->
        select * from emp where name like concat('%',#{name},'%') and 
      gender=#{gender} and entrydate between #{begin} and #{end} order by update_time desc
    </select>
</mapper>

EmpMapper.xml 中的各项属性要与 mapper 接口中的一致:

在这里插入图片描述
如果有不一致的,则 XML 映射文件无法匹配上对应的 mapper 接口方法。

MyBatis 动态 SQL

随着用户的输入或外部条件的变化而变化的 SQL 语句,称为动态 SQL

在前面的 SQL 代码中,查询条件都是固定好的,如果想通过只输入一个条件来查询是行不通的:

@Test
public void testList(){
	List<Emp> list = empMapper.list("张", null, null, null);
	System.out.println(list);
}

运行结果如下:

在这里插入图片描述

为了解决这样的问题,就可以使用动态 SQL

<if> 标签

<if>:用于判断条件是否成立,使用 test 属性进行条件判断,如果条件为 true,则拼接 SQL

使用 <if> 标签将代码修改为以下形式:

<mapper namespace="com.example.demo.mapper.EmpMapper">
    <select id="list" resultType="com.example.demo.pojo.Emp">
    <!--resultType 是单条记录封装的类型,要用对应实体类的全类名-->
        select * from emp
        where
            <if test="name != null">
                name like concat('%',#{name},'%')
            </if>
            <if test="gender != null">
                and gender = #{gender}
            </if>
            <if test="begin != null and end != null">
                and entrydate between #{begin} and #{end}
            </if>
        order by update_time desc
    </select>
</mapper>

测试类方法中只传入一个字段的数据:

@Test
public void testList(){
	List<Emp> list = empMapper.list("张", null, null, null);
	System.out.println(list);
}

这次查询成功:

在这里插入图片描述

但是新的问题又接踵而来,如果将 name 字段设置为 null,那么测试运行将会失败报错:

@Test
public void testList(){
	List<Emp> list = empMapper.list(null, (short)1, null, null);
	System.out.println(list);
}

控制台将会显示:

org.springframework.jdbc.BadSqlGrammarException: 
### Error querying database.  Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'and gender = 1
             
             
        order by update_time desc' at line 5
### The error may exist in com/example/demo/mapper/EmpMapper.xml
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: select * from emp         where                                             and gender = ?                                     order by update_time desc
### Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'and gender = 1
             
             
        order by update_time desc' at line 5
; bad SQL grammar []

这是因为当 name 字段为 null 时,不会被拼接进 SQL 语句,SQL 语句会变为 select * from emp where and gender=#{gender} order by update_time desc 这种具有语法错误的形式。

MyBatis 提供了 <where> 标签,使用该标签包裹着 <if> 标签即可解决这个问题:

<mapper namespace="com.example.demo.mapper.EmpMapper">
    <select id="list" resultType="com.example.demo.pojo.Emp">
    <!--resultType 是单条记录封装的类型,要用对应实体类的全类名-->
        select * from emp
        <where>
            <if test="name != null">
                name like concat('%',#{name},'%')
            </if>
            <if test="gender != null">
                and gender = #{gender}
            </if>
            <if test="begin != null and end != null">
                and entrydate between #{begin} and #{end}
            </if>
        </where>
        order by update_time desc
    </select>
</mapper>

这次运行测试就没有问题了:

在这里插入图片描述

同样的问题也存在于 update 更新语句中:

<update id="update">
    update emp 
  	set
    <if test="username != null">
        username=#{username},
    </if>
    <if test="name != null">
        name=#{name},
    </if>
    <if test="gender != null">
        gender=#{gender},
    </if>
   ......
    <if test="deptId != null">
        dept_id=#{deptId},
    </if>
    <if test="updateTime != null">
        update_time=#{updateTime}
    </if>
    where id=#{id}
</update>

如果说后面字段设置的值都为 null,那么就会变为 update emp set name=#{name},where id=#{id} 这种有语法错误的语句。

为了解决这个问题,MyBatis 提供了 <set> 标签:

<update id="update">
    update emp
    <set>
        <if test="username != null">
            username=#{username},
        </if>
        <if test="name != null">
            name=#{name},
        </if>
        <if test="gender != null">
            gender=#{gender},
        </if>
       ......
        <if test="deptId != null">
            dept_id=#{deptId},
        </if>
        <if test="updateTime != null">
            update_time=#{updateTime}
        </if>
    </set>
    where id=#{id}
</update>

使用 <set> 标签能让 MyBatis 的动态 update 语句更安全、更简洁,避免 SQL 错误和多余逗号的问题,是写更新语句时的推荐方式。

<foreach> 标签

<foreach> 标签多用于批量操作,<foreach> 标签中有几个属性需要了解:

  • collection:集合
  • item:集合中的元素
  • separator:分隔符
  • open:遍历开始前添加的内容
  • close:遍历结束后添加的内容

批量删除操作代码如下:

  • mapper 接口方法的代码:

    //批量删除员工
    public void deleteByIds(List<Integer> ids);
    
  • EmpMapper.xml 文件中的 SQL 代码:

    <delete id="deleteByIds">
    	delete from emp where id in
    		<foreach collection="ids" item="id" separator="," open="(" close=")">
    			#{id}
    		</foreach>
    </delete>
    
  • 测试类中的代码:

    @Test
    public void testDeleteByIds(){
    	List<Integer> ids = List.of(16, 18, 19);
    	empMapper.deleteByIds(ids);
    }
    
  • 运行结果如下:

    在这里插入图片描述

<sql> & <include> 标签

在下面两个 SQL 代码中存在一个问题:

<select id="list" resultType="com.example.demo.pojo.Emp">
    select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp
    where
    <if test="name != null">
        name like concat('%',#{name},'%')
    </if>
    <if test="gender != null">
        and gender = #{gender}
    </if>
    <if test="begin != null and end != null">
        and entrydate between #{begin} and #{end}
    </if>
    order by update_time desc
</select>
<select id="getById" resultType="com.example.demo.pojo.Emp">
    select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp
    where id = #{id}
</select>

其中的 select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp 在两段代码中都存在,之前学习 Java SE 的时候,我们会将类中重复的方法提取出来封装成一个公共类,而 MyBatis 提供了一种机制叫 SQL 片段(SQL Fragment),可以通过 <sql> 标签抽取重复的 SQL 内容,然后使用 <include> 引用。

将重复的片段提取出来:

<sql id="commonSelect">
    id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp
</sql>

然后在语句原本的位置用 <include> 引用,完整的 XML 映射文件代码如下:

<sql id="commonSelect">
    id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp
</sql>

<select id="list" resultType="com.example.demo.pojo.Emp">
    <include refid="commonSelect"/>
    where
    <if test="name != null">
        name like concat('%',#{name},'%')
    </if>
    <if test="gender != null">
        and gender = #{gender}
    </if>
    <if test="begin != null and end != null">
        and entrydate between #{begin} and #{end}
    </if>
    order by update_time desc
</select>

<select id="getById" resultType="com.example.demo.pojo.Emp">
    <include refid="commonSelect"/>
    where id = #{id}
</select>

网站公告

今日签到

点亮在社区的每一天
去签到