MyBatis 介绍与入门配置

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

官方参考:

1. MyBatis 解决什么问题?

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

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

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

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

例如有一个 Employee 类:

1
2
3
4
5
6
7
public class Employee {
private Integer id;
private String name;
private Character gender;
private Integer age;
private String homeAddress;
}

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

1
select * from employee where id = ?

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

2. 引入 MyBatis 依赖

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<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:mybatis MyBatis 框架本身
com.mysql:mysql-connector-j MySQL JDBC 驱动,让 Java 能连接 MySQL
junit-jupiter 用测试方法演示数据库操作

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

3. MyBatis 的基本运行流程

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

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

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

1
2
3
4
5
6
7
8
9
flowchart TD
A["mybatis-config.xml<br/>全局配置"] --> B["SqlSessionFactoryBuilder<br/>读取配置"]
B --> C["SqlSessionFactory<br/>会话工厂"]
C --> D["SqlSession<br/>一次数据库会话"]
D --> E["Mapper 接口代理"]
D --> F["字符串 SQL 标识"]
E --> G["Mapper XML 中的 SQL"]
F --> G
G --> H["数据库表"]

一句话概括:

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

4. 创建 SqlSessionFactory

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

1
2
3
4
5
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 语句的唯一标识:

1
2
3
4
5
6
7
8
9
10
11
@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" 由两部分组成:

1
namespace + "." + SQL 的 id

对应 XML 可以这样写:

1
2
3
4
5
6
<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">
参数 1 SQL 里的 #{id}

所以这句 Java 代码:

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

会找到这条 SQL:

1
select * from employee where id = #{id}

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

6. 更常用的 Mapper 接口方式

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

先定义接口:

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

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

1
2
3
4
5
6
<mapper namespace="com.bytepro.mapper.EmployeeMapper">
<select id="selectEmployee" resultType="emp">
select * from employee where id = #{id}
</select>

</mapper>

调用时这样写:

1
2
3
4
5
6
7
8
9
10
11
12
@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 的对应关系如下:

1
2
3
4
5
flowchart LR
A["EmployeeMapper 接口"] --> B["selectEmployee(Integer id)"]
B --> C["mapper.selectEmployee(1)"]
C --> D["XML: id=selectEmployee"]
D --> E["SQL: select * from employee where id = #{id}"]

接口方式的好处是:

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

7. SqlSession 用完要关闭

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

1
2
3
4
5
6
SqlSession session = factory.openSession();
try {
// 执行数据库操作
} finally {
session.close();
}

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

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

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

8. 增删改为什么要 commit?

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

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

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

1
SqlSession session = factory.openSession();

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

9. 这一篇先记住什么?

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