适合对象:第一次接触 MyBatis,想先把“它是什么、怎么跑起来、代码怎样调用 SQL”弄明白的同学。

官方参考:

1. MyBatis 解决什么问题?

如果只用原生 JDBC 操作数据库,通常要写很多重复代码:

  • 获取数据库连接
  • 创建 PreparedStatement
  • 给 SQL 设置参数
  • 执行 SQL
  • ResultSet 里取值
  • 手动把一行数据封装成 Java 对象
  • 关闭连接、语句、结果集

MyBatis 做的事情,可以简单理解为:

让 Java 方法和 SQL 语句建立对应关系,帮我们处理参数传递、SQL 执行和结果封装。

例如有一个 Employee 类:

public class Employee {
    private Integer id;
    private String name;
    private Character gender;
    private Integer age;
    private String homeAddress;
}

再有一张 employee 表。MyBatis 可以把下面这条 SQL 查询出来的数据,自动封装成 Employee 对象:

select * from employee where id = ?

这样,Java 代码就不用自己一列一列读取结果集了。

2. 引入 MyBatis 依赖

如果使用 Maven,可以引入 MyBatis、数据库驱动和测试依赖:

<dependency>
    <groupId>org.mybatis</groupId>

    <artifactId>mybatis</artifactId>

    <version>3.5.19</version>

</dependency>

<dependency>
    <groupId>com.mysql</groupId>

    <artifactId>mysql-connector-j</artifactId>

    <version>9.7.0</version>

</dependency>

<dependency>
    <groupId>org.junit.jupiter</groupId>

    <artifactId>junit-jupiter</artifactId>

    <version>RELEASE</version>

    <scope>test</scope>

</dependency>

这几类依赖的作用是:

依赖作用
org.mybatis:mybatisMyBatis 框架本身
com.mysql:mysql-connector-jMySQL JDBC 驱动,让 Java 能连接 MySQL
junit-jupiter用测试方法演示数据库操作

如果换成其他数据库,比如 PostgreSQL 或 Oracle,只需要换成对应数据库的 JDBC 驱动。

3. MyBatis 的基本运行流程

MyBatis 入门时最重要的三个对象是:

对象作用
SqlSessionFactoryBuilder读取配置,创建 SqlSessionFactory
SqlSessionFactory创建 SqlSession 的工厂
SqlSession表示一次数据库会话,用它执行 SQL

它们的关系可以这样理解:

mybatis-config.xml
全局配置
SqlSessionFactoryBuilder
读取配置
SqlSessionFactory
会话工厂
SqlSession
一次数据库会话
Mapper 接口代理
字符串 SQL 标识
Mapper XML 中的 SQL
数据库表

一句话概括:

先读取配置创建 SqlSessionFactory,再打开 SqlSession,最后通过 SqlSession 找到 XML 里的 SQL 并执行。

4. 创建 SqlSessionFactory

MyBatis 官方文档中说,每个基于 MyBatis 的应用都以 SqlSessionFactory 为核心。常见写法如下:

public SqlSessionFactory getSqlSessionFactory() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    return new SqlSessionFactoryBuilder().build(inputStream);
}

这段代码做了三件事:

  1. 指定 MyBatis 全局配置文件:mybatis-config.xml
  2. Resources.getResourceAsStream(resource) 从 classpath 读取配置
  3. SqlSessionFactoryBuilder 构建 SqlSessionFactory

可以把 SqlSessionFactory 理解成“数据库会话工厂”。后面每次要操作数据库,都从它这里打开一个 SqlSession

5. 使用 SqlSession 直接执行 SQL

第一种调用方式是直接使用 SQL 语句的唯一标识:

@Test
public void testSelectOne() throws IOException {
    SqlSessionFactory factory = getSqlSessionFactory();
    SqlSession session = factory.openSession();
    try {
        Employee employee = session.selectOne("EmployeeMapper.selectEmployee", 1);
        System.out.println(employee);
    } finally {
        session.close();
    }
}

"EmployeeMapper.selectEmployee" 由两部分组成:

namespace + "." + SQL 的 id

对应 XML 可以这样写:

<mapper namespace="EmployeeMapper">
    <select id="selectEmployee" resultType="com.bytepro.entity.Employee">
        select * from employee where id = #{id}
    </select>

</mapper>

这表示:

Java 调用内容XML 中对应位置
EmployeeMapper<mapper namespace="EmployeeMapper">
selectEmployee<select id="selectEmployee">
参数 1SQL 里的 #{id}

所以这句 Java 代码:

session.selectOne("EmployeeMapper.selectEmployee", 1);

会找到这条 SQL:

select * from employee where id = #{id}

然后把参数 1 传给 #{id}

6. 更常用的 Mapper 接口方式

直接写字符串能运行,但开发中更推荐 Mapper 接口方式。

先定义接口:

public interface EmployeeMapper {
    Employee selectEmployee(Integer id);
}

再让 XML 的 namespace 对应接口全类名,SQL 的 id 对应接口方法名:

<mapper namespace="com.bytepro.mapper.EmployeeMapper">
    <select id="selectEmployee" resultType="emp">
        select * from employee where id = #{id}
    </select>

</mapper>

调用时这样写:

@Test
public void testSelectInterface() throws IOException {
    SqlSessionFactory factory = getSqlSessionFactory();
    SqlSession session = factory.openSession();
    try {
        EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
        Employee employee = mapper.selectEmployee(1);
        System.out.println(employee);
    } finally {
        session.close();
    }
}

接口、XML、SQL 的对应关系如下:

EmployeeMapper 接口
selectEmployee(Integer id)
mapper.selectEmployee(1)
XML: id=selectEmployee
SQL: select * from employee where id = #{id}

接口方式的好处是:

  • 方法名有代码提示
  • 参数和返回值类型清楚
  • 少写字符串,减少拼错 SQL 标识的机会
  • 调用方式更像普通 Java 方法

7. SqlSession 用完要关闭

SqlSession 代表一次数据库会话,它不是线程安全的,也不应该长期共享。使用完后要关闭:

SqlSession session = factory.openSession();
try {
    // 执行数据库操作
} finally {
    session.close();
}

也可以使用 Java 的 try-with-resources

try (SqlSession session = factory.openSession()) {
    EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
    Employee employee = mapper.selectEmployee(1);
    System.out.println(employee);
}

这样写的目的都是一样的:保证数据库会话能被正确释放。

8. 增删改为什么要 commit?

查询只是读取数据,一般不需要手动提交事务。新增、修改、删除会改变数据库数据,需要提交:

Long result = mapper.insertEmployee(employee);
session.commit();

如果用下面这种方式打开会话:

SqlSession session = factory.openSession();

默认不是自动提交。执行 insertupdatedelete 后,如果没有调用 session.commit(),修改可能不会真正保存到数据库。

9. 这一篇先记住什么?

  • MyBatis 用来建立 Java 方法和 SQL 语句之间的映射关系。
  • SqlSessionFactoryBuilder 读取配置,创建 SqlSessionFactory
  • SqlSessionFactory 用来打开 SqlSession
  • SqlSession 用来执行 SQL,用完必须关闭。
  • Mapper 接口方式比字符串方式更常用。
  • XML 中的 namespace + id 决定 Java 代码能找到哪条 SQL。