适合对象:第一次接触 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: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 |
它们的关系可以这样理解:
一句话概括:
先读取配置创建
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);
}
这段代码做了三件事:
- 指定 MyBatis 全局配置文件:
mybatis-config.xml - 用
Resources.getResourceAsStream(resource)从 classpath 读取配置 - 用
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"> |
| 参数 1 | SQL 里的 #{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 的对应关系如下:
接口方式的好处是:
- 方法名有代码提示
- 参数和返回值类型清楚
- 少写字符串,减少拼错 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();
默认不是自动提交。执行 insert、update、delete 后,如果没有调用 session.commit(),修改可能不会真正保存到数据库。
9. 这一篇先记住什么?
- MyBatis 用来建立 Java 方法和 SQL 语句之间的映射关系。
SqlSessionFactoryBuilder读取配置,创建SqlSessionFactory。SqlSessionFactory用来打开SqlSession。SqlSession用来执行 SQL,用完必须关闭。- Mapper 接口方式比字符串方式更常用。
- XML 中的
namespace + id决定 Java 代码能找到哪条 SQL。
评论