从源码深入理解Mybatis

从源码深入理解Mybatis

一、前言

1.1 mybatis简述

myBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射,几乎免除了所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO为数据库中的记录。

​ 官网地址:https://mybatis.org/mybatis-3/zh_CN/index.html

​ Github仓库:

1.2 核心功能与优势

myBatis 的核心在于其灵活性简化性

  • 灵活的自定义SQL:允许开发者编写精确的SQL,优化数据库操作性能。
  • 简化JDBC操作:封装了JDBC的繁琐流程(如连接管理、语句准备、结果集处理),开发者只需关注SQL本身和对象映射
  • 强大的映射能力:提供多种结果映射方式,轻松处理数据库字段与Java属性名不一致的情况
  • 动态SQL支持:通过XML标签构建灵活查询,避免拼接SQL字符串的繁琐与错误

二、使用JDBC操作数据库

JDBC (Java Database Connectivity) 是 Java 提供的一套用于执行 SQL 语句的 API,它允许 Java 程序与各种关系型数据库进行连接和交互。JDBC 的工作原理基于”驱动程序管理器”模式,主要包括以下几个核心组件:

  1. JDBC API:提供应用程序到 JDBC 管理器之间的连接
  2. JDBC Driver Manager:管理不同数据库的驱动程序
  3. JDBC Driver:由数据库厂商提供,实现与特定数据库的通信
  4. 数据库:实际存储数据的地方

image-20250921115830009

2.1 代码示例

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
public class JDBCDemo {
// 数据库连接信息 - 请根据您的环境修改这些值
private static final String JDBC_URL = "jdbc:mysql://localhost:3306/user";
private static final String USERNAME = "root";
private static final String PASSWORD = "";


public static void main(String[] args) throws Exception {
List<User> userList = new ArrayList<>();

Class.forName("com.mysql.jdbc.Driver");
try (Connection conn = DriverManager.getConnection(JDBC_URL, USERNAME, PASSWORD)) {
try (Statement stmt = conn.createStatement()) {
try (ResultSet rs = stmt.executeQuery("SELECT id,name,age FROM user WHERE id=1;")) {
while (rs.next()) {
Integer id = rs.getInt(1); // 注意:索引从1开始
String name = rs.getString(2);
Integer age = rs.getInt(3);
User user = new User(id,name,age);
userList.add(user);
}
}
}
}
System.out.println("查询出用户数量:" + userList.size());
}
}

image-20250921114305498

2.2 为什么需要加载驱动程序

1
Class.forName("com.mysql.jdbc.Driver");

​ 在使用数据库时,需要加载数据库驱动是因为数据库驱动是连接数据库和应用程序之间的桥梁。数据库驱动提供了与特定数据库通信所需的功能和方法,使得应用程序能够与数据库进行交互。

​ 加载数据库驱动的过程实际上是将数据库驱动程序的类加载到JVM中使得应用程序可以使用驱动程序中的方法和功能。通过加载数据库驱动,应用程序可以建立与数据库的连接,执行SQL语句,读取和写入数据库中的数据等操作。

三、mybatis入门使用

​ 我们先简单了解下mybatis的使用,看看相比原生的JDBC到底好在哪里。

3.1 mybatis使用

pom文件引入依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependencies>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!-- Druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
</dependencies>

mybatis配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/user?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/jiang/UserMapper.xml"/>
</mappers>
</configuration>

Mapper接口以及xml文件:

1
2
3
public interface UserMapper {
UserPo getUserById(int id);
}
1
2
3
4
5
6
7
8
9
<?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.jiang.dao.mapper.UserMapper">
<select id="getUserById" resultType="com.jiang.dao.po.UserPo">
SELECT * FROM user WHERE id = #{id}
</select>
</mapper>

使用 MyBatis 进行数据库操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class UserTest {

public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml"; // 配置文件路径
InputStream inputStream = Resources.getResourceAsStream(resource); // 获取配置文件流
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 创建 SqlSessionFactory 对象
SqlSession session = sqlSessionFactory.openSession(); // 打开会话(Session)对象用于执行SQL命令和获取结果。
UserMapper mapper = session.getMapper(UserMapper.class);
UserPo userById = mapper.getUserById(1);
System.out.println("id:" + userById.getId());
System.out.println("姓名:" + userById.getName());
System.out.println("年龄:" + userById.getAge());
}
}

mybatis查询结果

3.2 mybatis与JDBC对比

简单来说:

  • JDBC (Java Database Connectivity) 是 Java 提供的、用于连接和操作所有关系型数据库的标准 API基础。它是一个底层规范。
  • MyBatis 是一个构建在 JDBC 之上的持久层框架。它封装了 JDBC 的复杂和繁琐操作,让你用更简单、更高效的方式操作数据库。

我们用一个表格来详细对比下mybatis和JDBC的特性:

维度 JDBC MyBatis
本质 标准接口和规范 基于 JDBC 的持久层框架
SQL 管理 SQL 硬编码在 Java 代码中,难以维护 SQL 与 Java 代码解耦,写在 XML 或注解中
结果映射 手动编写代码从 ResultSet 中获取数据并塞入对象,非常繁琐 自动映射:将查询结果集自动转换为 Java 对象(POJO)
开发效率 ,需要编写大量样板代码(创建连接、处理异常、关闭资源等) ,框架处理了大部分重复性工作,开发者专注业务 SQL
性能 手动控制,理论上可以做到最优,但需要开发者有很高水平 内置连接池、缓存等优化机制,开箱即用的性能较好
数据库兼容性 最好,因为是标准接口,所有数据库都提供 JDBC 驱动 很好,MyBatis 底层仍是 JDBC,但某些数据库特定语法可能需要调整 SQL
学习成本 较低,核心是几个固定接口(Connection, Statement, ResultSet) 中等,需要学习框架的配置、Mapper 接口、XML 标签等
灵活性 极致灵活,可以完全控制每一个细节和 SQL 执行过程 灵活但受约束,对复杂和高度优化的 SQL 支持极好,但框架本身有一定约定
社区和生态 是 Java 标准的一部分,所有数据库操作都基于它 拥有强大的社区和丰富的第三方插件(如分页插件、代码生成器)

3.3 mybatis全局配置文件

​ 我们在mybatis代码中,我们会配置一个全局配置文件mybatis-config.xml,它包含了影响 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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

<properties resource="db.properties"/> <!-- 1. 属性 -->

<settings> <!-- 2. 设置 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

<typeAliases> <!-- 3. 类型别名 -->
<package name="com.example.model"/>
</typeAliases>

<typeHandlers> <!-- 4. 类型处理器 -->
</typeHandlers>

<objectFactory type=""> <!-- 5. 对象工厂 -->
</objectFactory>

<plugins> <!-- 6. 插件 -->
</plugins>

<environments default="development"> <!-- 7. 环境配置 -->
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>

<databaseIdProvider type="DB_VENDOR"> <!-- 8. 数据库厂商标识 -->
</databaseIdProvider>

<mappers> <!-- 9. 映射器 -->
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
3.3.1 properties属性

用于引入外部属性文件(如数据库连接信息),以便在配置中使用 ${} 占位符来动态替换值。这实现了配置与代码的分离。

  • **resource (常用)**:从类路径(classpath)下加载属性文件。
  • **url**:使用完整的 URL 路径加载属性文件(如 file:///var/config/db.properties)。
  • 可以直接内嵌 <property> 子标签,但不如外部文件灵活。

xml

1
<properties resource="jdbc.properties"/>

jdbc.properties 文件中:

properties

1
2
3
4
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_db
jdbc.username=root
jdbc.password=secret
3.3.2 settings设置

这是 MyBatis 中最为重要和复杂的配置部分,它调整 MyBatis 的运行时行为。每个设置都有默认值。

常用设置名 描述 有效值 默认值
cacheEnabled 全局开启或关闭缓存 true/false true
lazyLoadingEnabled 开启延迟加载(按需加载关联对象) true/false false
mapUnderscoreToCamelCase 重要:自动将下划线命名映射为驼峰命名(user_name -> userName true/false false
logImpl 指定 MyBatis 使用的日志框架 SLF4J/LOG4J2/STDOUT_LOGGING 未设置
defaultExecutorType 配置默认的执行器 SIMPLE/REUSE/BATCH SIMPLE

xml

1
2
3
4
5
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="logImpl" value="SLF4J"/>
</settings>
3.3.3 typeAliases 类型别名

为 Java 类型设置一个简短的名称,避免在 Mapper XML 中反复输入冗长的完全限定类名。

  • **<typeAlias>**:为单个类设置别名。
  • <package> (常用):扫描整个包,默认别名为该包下类的类名首字母小写(如 User 的别名是 user)。使用 @Alias("myUser") 注解可以自定义别名。

xml

1
2
3
4
5
6
<typeAliases>
<!-- 方式一:逐个定义 -->
<typeAlias alias="User" type="com.example.model.User"/>
<!-- 方式二:扫描包(推荐) -->
<package name="com.example.model"/>
</typeAliases>

之后在 Mapper XML 中,resultType="user" 即可,而不用写 resultType="com.example.model.User"

3.3.4 typeHandlers 类型处理器

用于在 Java 类型和 JDBC 类型之间进行转换。MyBatis 内置了大部分常用类型的处理器(如 String、Integer 等)。这个配置通常只在需要处理自定义类型(如枚举、JSON 对象)时才使用。

3.3.5 environments 环境配置

核心中的核心,用于配置数据库连接。default 属性指定默认使用的环境 ID。

  • **<environment>**:定义一个环境,id 是该环境的唯一标识。
    • **<transactionManager>**:事务管理器。
      • type="JDBC":使用 JDBC 的事务管理(默认,推荐)。
      • type="MANAGED":几乎什么都不做,让容器(如 Spring)来管理事务。
    • **<dataSource>**:数据源,负责连接数据库。
      • type="UNPOOLED":每次请求都打开和关闭连接,速度慢。
      • type="POOLED" (最常用):使用连接池,优化性能。
      • type="JNDI":从容器(如 Tomcat)中获取数据源。

重要提示:在 Spring 等框架集成 MyBatis 后,事务管理和数据源通常会交由 Spring 来管理,此部分的配置会被忽略。

3.3.6 mappers 映射器

告诉 MyBatis 去哪里寻找定义 SQL 的 Mapper XML 文件或接口。这是必须配置的。

有四种指定方式:

  • **<mapper resource=""> (最常用)**:从类路径查找 XML 文件。
  • **<mapper url="">**:通过绝对文件路径查找。
  • **<mapper class="">**:使用注解的 Mapper 接口。要求 XML 和接口同名同路径。
  • **<package name=""> (推荐)**:扫描包下的所有 Mapper 接口。

xml

1
2
3
4
5
6
7
8
<mappers>
<!-- 方式一:指定XML资源路径 -->
<mapper resource="org/mybatis/mapper/UserMapper.xml"/>
<!-- 方式二:指定接口类(配合注解使用) -->
<mapper class="org.mybatis.mapper.UserMapper"/>
<!-- 方式三:扫描整个包(最省事) -->
<package name="org.mybatis.mapper"/>
</mappers>

四、mybatis架构&组件

在分析mybatis源码之前

4.1 mybatis架构

​ MyBatis 的架构是典型的分层架构,职责清晰,模块化程度高。其核心架构可以划分为三大层:

接口层核心处理层基础支撑层

mybatis架构

架构层次 核心组件 主要职责
接口层 SqlSession MapperProxy 应用程序与 MyBatis 框架交互的窗口和桥梁。目标是简化API,让开发者能够以最自然的方式(面向接口编程)进行数据库操作,而无需关心底层JDBC的复杂细节。
核心处理层 Executor MappedStatement StatementHandler 执行SQL语句的核心流程,包括参数处理、SQL执行、结果映射。
基础支撑层 Transaction DataSource Cache TypeHandler 为上层核心处理提供通用的、可复用的技术服务。

4.2 mybatis核心组件

我们根据上述mybatis架构的层级来简单介绍下mybatis组件。

4.2.1 接口层 - SqlSession

SqlSession是MyBatis工作的主要顶层接口,代表了一次数据库会话。你可以将它视为一个数据库命令执行器

SqlSession是非线程安全,每次请求都应创建新的实例,使用完毕后必须关闭。它的生命周期通常对应一次数据库事务或一次请求。

SqlSession会提供的方法:

  • selectOne, selectList, insert, update, delete:直接执行SQL映射语句。
  • commit, rollback:手动提交或回滚事务。
  • getMapper(Class<T> type)这是最核心、最常用的方法,用于获取Mapper接口的动态代理对象。
4.2.2 接口层 - MapperProxy

MapperProxy是是MyBatis实现“接口式编程”的魔法核心。它通过JDK动态代理技术,为开发者定义的Mapper接口生成一个代理实例。

SqlSession工作流程

  1. 当应用程序调用 userMapper.getUserById(1) 时,实际上调用的是 MapperProxyinvoke 方法。
  2. MapperProxy 会拦截这次方法调用,并解析出方法名、参数等信息。
  3. 然后,它将这些信息转换为对 SqlSession 底层方法(如 selectOne)的调用,并将具体的SQL执行任务委托给下一层(核心处理层)。
4.2.2 核心处理层 - Configuration

​ Configuration是MyBatis的“大脑”。在应用启动时,它会解析所有的 mybatis-config.xmlMapper.xml 文件,将所有配置信息(数据源、事务、Mapper语句、类型处理器等)存储在一个全局唯一的 Configuration 对象中。它是整个框架运行时的配置信息仓库

4.2.3 核心处理层 - Executor

Executor是SQL执行的调度器SqlSession 只是门面,真正执行数据库操作的是 Executor

  • Executor类型与策略
    • SimpleExecutor:默认,每次执行都创建一个新的 PreparedStatement
    • ReuseExecutor:复用 PreparedStatement 对象。
    • BatchExecutor:用于批处理操作。
    • CachingExecutor:装饰器模式,为其他执行器增加二级缓存功能。
  • Executor工作流程
    • Executor 会按顺序调用 StatementHandlerParameterHandlerResultSetHandler 来完成SQL执行。
4.2.4 核心处理层 - MappedStatement

封装了一条SQL语句的所有信息。每一个 <select|insert|update|delete> 标签都会被解析成一个 MappedStatement 对象,并以其唯一ID(如 com.jiang.UserMapper.selectById)为Key,存储在 Configuration 中。它是SQL在MyBatis内部的运行时表示

4.2.5 核心处理层 - ResultSetHandler

​ 负责将JDBC返回的 ResultSet 结果集,映射成我们定义的Java对象。这是ORM(对象关系映射)的核心实现。它会根据 <resultMap> 配置,使用 TypeHandler 将列值填充到对象的属性中。

4.2.6 基础支撑层 - Transaction

Transaction是管理数据库事务的生命周期,包括创建、提交、回滚和关闭连接。

Transaction实现:

  • JdbcTransaction:使用JDBC原生事务管理。
  • ManagedTransaction:将事务管理权交给容器(如Spring),自己不做任何处理。
4.2.7 基础支撑层 - DataSource

DataSource是管理数据库连接,是连接池技术的抽象。它负责高效地创建、管理和回收数据库连接,避免了频繁创建和销毁连接的开销。

DataSource实现了MyBatis内置了简单实现,但生产环境通常集成Druid、HikariCP等高性能连接池。

4.3 总结

通过这三层架构,MyBatis实现了清晰的职责分离

  • 接口层 关注做什么(What),为开发者提供友好的API。
  • 核心处理层 关注怎么做(How),负责SQL执行的核心逻辑。
  • 基础支撑层 关注用什么做(With What),提供底层的技术能力和扩展点。

这种分层设计使得MyBatis在保持了强大功能和灵活性的同时,也具备了良好的可维护性和可扩展性。

五、源码解析 - Mybatis初始化源码解析

5.1 初始化入口:SqlSessionFactoryBuilder

Mybatis的初始化通常从SqlSessionFactoryBuilder.build()方法开始。无论是使用XML配置文件还是Java配置,最终都会进入这个方法。

1
2
3
4
// 典型用法
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

SqlSessionFactoryBuilder.build()方法的核心逻辑是创建XMLConfigBuilder解析器,然后调用parse()方法得到Configuration对象,最后创建DefaultSqlSessionFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 创建XMLConfigBuilder解析器
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 解析配置得到Configuration对象,然后创建SqlSessionFactory
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}

5.2 XMLConfigBuilder解析全局配置文件

XMLConfigBuilder是专门用于解析mybatis-config.xml配置文件的构建器。它继承自BaseBuilder,使用XPathParser解析XML文档。

parse()方法调用parseConfiguration()方法,从根节点开始逐个解析配置文件的不同部分:

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
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 从根节点开始解析
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}

private void parseConfiguration(XNode root) {
try {
// 按顺序解析各个配置节点
propertiesElement(root.evalNode("properties"));
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(root.evalNode("settings"));
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}

5.3 解析Mapper映射文件

在parseConfiguration()方法中,mapperElement(root.evalNode(“mappers”))负责解析mapper映射文件。mappers标签可以配置四种方式引入Mapper:resource、url、class和package。

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
29
30
31
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// 扫描包下的所有Mapper接口
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");

if (resource != null && url == null && mapperClass == null) {
// 通过resource加载XML文件
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
// 通过url加载XML文件
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
// 通过class加载Mapper接口(注解方式)
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
}
}
}
}
}

5.4 XMLMapperBuilder解析Mapper文件

XMLMapperBuilder负责解析具体的Mapper XML文件。它的parse()方法会检查文件是否已经加载过,然后解析namespace和具体的SQL语句。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
public void parse() {
// 检查是否已经加载过
if (!configuration.isResourceLoaded(resource)) {
// 解析mapper节点的namespace和cache等
configurationElement(parser.evalNode("/mapper"));
// 标记为已加载
configuration.addLoadedResource(resource);
// 将namespace绑定到MapperRegistry
bindMapperForNamespace();
}

// 解析未完成的语句
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}

private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);

// 解析cache-ref
cacheRefElement(context.evalNode("cache-ref"));
// 解析cache
cacheElement(context.evalNode("cache"));
// 解析parameterMap(已废弃,但仍有兼容代码)
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// 解析resultMap
resultMapElements(context.evalNodes("/mapper/resultMap"));
// 解析sql片段
sqlElement(context.evalNodes("/mapper/sql"));
// 解析select|insert|update|delete语句
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}

5.5 XMLStatementBuilder解析SQL语句

​ buildStatementFromContext()方法会创建XMLStatementBuilder来解析每个SQL标签,最终创建MappedStatement对象。

1
2
3
4
5
6
7
8
9
10
11
12
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
// 为每个SQL标签创建XMLStatementBuilder
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
// 解析SQL节点
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}

​ parseStatementNode()方法会解析SQL标签的各种属性,如id、parameterType、resultType等,然后使用LanguageDriver解析SQL内容,最后通过MapperBuilderAssistant创建MappedStatement并注册到Configuration中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void parseStatementNode() {
// 解析各种属性
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
String parameterType = context.getStringAttribute("parameterType");
String resultType = context.getStringAttribute("resultType");
String resultMap = context.getStringAttribute("resultMap");
// ... 更多属性解析

// 获取LanguageDriver
LanguageDriver langDriver = getLanguageDriver(context.getStringAttribute("language"));

// 解析SQL语句,创建SqlSource
XmlScriptBuilder builder = new XmlScriptBuilder(configuration, context, parameterTypeClass);
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);

// 使用BuilderAssistant创建MappedStatement
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}

5.6 MappedStatement的创建

MapperBuilderAssistant.addMappedStatement()方法负责创建MappedStatement对象并将其添加到Configuration中。

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
29
30
31
32
33
34
35
public MappedStatement addMappedStatement(
String id,
SqlSource sqlSource,
StatementType statementType,
SqlCommandType sqlCommandType,
// ... 其他参数
) {
// 添加namespace前缀
id = applyCurrentNamespace(id, false);

// 使用建造者模式创建MappedStatement
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
.resource(resource)
.fetchSize(fetchSize)
.timeout(timeout)
.statementType(statementType)
// ... 设置其他属性

// 如果有ResultMap,设置ResultMap
if (resultMap != null) {
String[] resultMapIds = resultMap.split(",");
Set<String> resultMapSet = new HashSet<String>();
for (String resultMapId : resultMapIds) {
resultMapId = applyCurrentNamespace(resultMapId, true);
resultMapSet.add(resultMapId);
}
statementBuilder.resultMaps(resultMapSet);
}

// 构建MappedStatement
MappedStatement statement = statementBuilder.build();
// 注册到Configuration
configuration.addMappedStatement(statement);
return statement;
}

至此,Mybatis的初始化阶段完成,Configuration对象中包含了所有解析后的配置信息、Mapper映射信息,为后续的SQL执行做好了准备。

六、Mapper代理实现源码解析

6.1 Mapper接口注册过程

在初始化阶段,无论是通过XML的<mapper class="..."/>配置,还是通过@MapperScan注解,最终都会调用Configuration.addMapper()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public <T> void addMapper(Class<T> type) {
// Mapper必须是接口
if (type.isInterface()) {
if (hasMapper(type)) {
throw new RuntimeException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 将Mapper接口和对应的代理工厂放入knownMappers
knownMappers.put(type, new MapperProxyFactory<T>(type));

// 解析注解方式的SQL语句
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}

6.2 MapperAnnotationBuilder解析注解

MapperAnnotationBuilder负责解析Mapper接口方法上的注解(如@Select、@Insert等),将其转换为MappedStatement。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
// 设置namespace
assistant.setCurrentNamespace(type.getName());

// 解析所有方法
Method[] methods = type.getMethods();
for (Method method : methods) {
if (!method.isBridge()) {
// 解析语句
parseStatement(method);
}
}
}
}

private void parseStatement(Method method) {
// 获取参数类型
Class<?> parameterTypeClass = getParameterType(method);

// 获取LanguageDriver
LanguageDriver languageDriver = getLanguageDriver(method);

// 从注解获取SqlSource
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);

if (sqlSource != null) {
final String mappedStatementId = type.getName() + "." + method.getName();
SqlCommandType sqlCommandType = getSqlCommandType(method);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

String resultMapId = null;
if (isSelect) {
// 解析结果映射
resultMapId = parseResultMap(method);
}

// 使用BuilderAssistant创建MappedStatement
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
// ... 其他参数
);
}
}

3.3 代理对象的创建:MapperProxyFactory

当调用SqlSession.getMapper(Class type)方法时,Mybatis会从MapperRegistry中获取对应的MapperProxyFactory,然后创建代理对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 从knownMappers获取代理工厂
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 创建代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}

MapperProxyFactory使用JDK动态代理创建代理对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}

public T newInstance(SqlSession sqlSession) {
// 创建MapperProxy,它实现了InvocationHandler
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
// 使用JDK动态代理创建代理对象
return newInstance(mapperProxy);
}

protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),
new Class[] { mapperInterface }, mapperProxy);
}
}

3.4 代理逻辑:MapperProxy

MapperProxy实现了InvocationHandler接口,当调用Mapper接口的方法时,会进入invoke方法。

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
29
30
31
32
33
34
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 如果是Object的方法,直接调用
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
// 如果是默认方法,特殊处理
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}

// 从缓存获取或创建MapperMethod,然后执行
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}

private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}

3.5 MapperMethod封装

MapperMethod封装了方法对应的SQL命令类型和接口方法信息,它的execute方法会根据SQL命令类型调用SqlSession的相应方法。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class MapperMethod {
private final SqlCommand command;
private final MethodSignature method;

public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
// 有结果处理器的方法
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
// 返回集合的方法
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
// 返回Map的方法
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
// 返回Cursor的方法
result = executeForCursor(sqlSession, args);
} else {
// 返回单个对象
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
return result;
}
}

七、SQL执行流程源码深度解析

7.1 SqlSession的实现类DefaultSqlSession

DefaultSqlSession是SqlSession接口的默认实现,它持有Configuration和Executor引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class DefaultSqlSession implements SqlSession {
private final Configuration configuration;
private final Executor executor;
private final boolean autoCommit;
private boolean dirty;
private List<Cursor<?>> cursorList;

@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 从Configuration获取MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
// 委托给Executor执行
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}

7.2 Executor体系结构

Executor是Mybatis的执行器接口,定义了数据库操作的基本方法。它有多个实现类,形成了模板方法模式和装饰器模式的典型应用。

  • BaseExecutor:抽象类,实现了Executor接口的大部分方法,主要提供缓存管理和事务管理的能力。
  • SimpleExecutor:默认执行器,每次执行都会创建一个Statement,用完后关闭。
  • ReuseExecutor:可重用执行器,将Statement存入Map中,操作Map中的Statement而不会重复创建。
  • BatchExecutor:批量执行所有更新语句,基于JDBC的batch操作实现批处理。
  • CachingExecutor:使用装饰器模式,对真正提供数据库查询的Executor增强了二级缓存的能力。

7.3 查询入口:CachingExecutor.query()

当开启了二级缓存时,首先会进入CachingExecutor的query方法。

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
29
30
31
32
33
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 获取BoundSql
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 创建缓存Key
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
// 从MappedStatement获取二级缓存
Cache cache = ms.getCache();
if (cache != null) {
// 如果需要清空缓存
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
// 确保输出参数和更新操作不进入缓存
ensureNoOutParams(ms, parameterObject, boundSql);
// 从二级缓存获取
@SuppressWarnings("unchecked")
List<E> cachedList = (List<E>) cache.getObject(key);
if (cachedList == null) {
// 缓存未命中,委托给实际Executor查询
cachedList = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
// 存入缓存
cache.putObject(key, cachedList);
}
return cachedList;
}
}
// 无二级缓存,直接委托
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

7.4 BaseExecutor的模板方法

BaseExecutor是一个抽象类,实现了缓存的共用逻辑,将实际的doQuery方法留给子类实现。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public abstract class BaseExecutor implements Executor {
protected Transaction transaction;
protected Executor wrapper;
protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
protected PerpetualCache localCache; // 一级缓存
protected PerpetualCache localOutputParameterCache;
protected Configuration configuration;

@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache(); // 清空一级缓存
}
List<E> list;
try {
queryStack++;
// 从一级缓存获取
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
// 缓存命中,处理存储过程输出参数
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 缓存未命中,从数据库查询
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache(); // 如果是语句级别的缓存,清空
}
}
return list;
}

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
// 先放入占位符,防止重复查询
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
// 调用子类的doQuery方法
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
// 移除占位符
localCache.removeObject(key);
}
// 放入缓存
localCache.putObject(key, list);
return list;
}

protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException;
}

7.5 SimpleExecutor.doQuery

SimpleExecutor是Executor的默认实现,它的doQuery方法创建StatementHandler并执行查询。

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
29
public class SimpleExecutor extends BaseExecutor {

@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 创建StatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 准备Statement
stmt = prepareStatement(handler, ms.getStatementLog());
// 执行查询
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 获取数据库连接
Connection connection = getConnection(statementLog);
// 创建Statement
stmt = handler.prepare(connection, transaction.getTimeout());
// 设置参数
handler.parameterize(stmt);
return stmt;
}
}

7.6 StatementHandler体系

StatementHandler负责创建Statement、设置参数、执行SQL和处理结果集。它有多个实现类:

  • RoutingStatementHandler:路由StatementHandler,根据MappedStatement的StatementType创建对应的StatementHandler。
  • SimpleStatementHandler:处理JDBC Statement。
  • PreparedStatementHandler:处理PreparedStatement(最常用)。
  • CallableStatementHandler:处理存储过程。

Configuration.newStatementHandler()方法创建的是RoutingStatementHandler,它会根据配置路由到具体的实现:

1
2
3
4
5
6
7
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 创建RoutingStatementHandler,它会根据mappedStatement.getStatementType()创建对应的StatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 应用插件(拦截器)
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}

RoutingStatementHandler的构造函数会根据StatementType创建对应的StatementHandler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
}

7.7 PreparedStatementHandler执行流程

PreparedStatementHandler是最常用的StatementHandler,它处理PreparedStatement。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class PreparedStatementHandler extends BaseStatementHandler {

@Override
public void parameterize(Statement statement) throws SQLException {
// 使用ParameterHandler设置参数
parameterHandler.setParameters((PreparedStatement) statement);
}

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 执行SQL
ps.execute();
// 使用ResultSetHandler处理结果集
return resultSetHandler.<E> handleResultSets(ps);
}
}

7.8 ParameterHandler参数设置

ParameterHandler负责将Java参数设置到PreparedStatement中。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class DefaultParameterHandler implements ParameterHandler {
private final TypeHandlerRegistry typeHandlerRegistry;
private final MappedStatement mappedStatement;
private final Object parameterObject;
private final BoundSql boundSql;
private final Configuration configuration;

@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 获取参数映射列表
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
// 从参数对象中获取属性值
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 获取类型处理器
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
// 设置参数
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}
}

4.9 ResultSetHandler结果集处理

ResultSetHandler负责将ResultSet映射为Java对象。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public class DefaultResultSetHandler implements ResultSetHandler {

@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);

// 获取ResultMap列表
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
if (rsw != null && resultMapCount > 0) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 处理结果集
handleResultSet(rsw, resultMap, multipleResults, null);
}

// 处理多个结果集的情况
while (rsw != null && rsw.hasMoreResults()) {
// ... 处理剩余结果集
}

return collapseSingleResultList(multipleResults);
}

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
if (resultHandler == null) {
// 默认结果处理器
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
// 处理行值
handleRowValuesForSimpleResultMap(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, null);
}
}

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
while (resultContext.getResultCount() < rowBounds.getLimit() && rsw.getResultSet().next()) {
// 获取行值
Object rowValue = getRowValue(rsw, resultMap);
// 存储结果
resultHandler.handleResult(resultContext.createResultObject(rowValue));
}
}

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
// 创建结果对象
Object resultObject = createResultObject(rsw, resultMap, null);
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(resultObject);
// 自动映射
boolean foundValues = resultMap.getConstructorResultMappings().size() > 0;
if (shouldApplyAutomaticMappings(resultMap, true)) {
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
}
// 属性映射
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, null) || foundValues;
}
return resultObject;
}
}

八、SQL解析与动态SQL源码解析

8.1 SqlSource与BoundSql

SqlSource是SQL语句的来源接口,负责根据参数对象生成BoundSql。BoundSql则封装了最终的SQL语句、参数映射和参数值

1
2
3
4
5
6
7
8
9
10
public interface SqlSource {
BoundSql getBoundSql(Object parameterObject);
}

public class BoundSql {
private final String sql; // 解析后的SQL语句(带?占位符)
private final List<ParameterMapping> parameterMappings; // 参数映射
private final Object parameterObject; // 原始参数对象
private final Map<String, Object> additionalParameters; // 附加参数
}

5.2 SqlSource的两种类型

根据SQL语句是否包含动态内容(如标签或${}),Mybatis会创建不同类型的SqlSource:

  • DynamicSqlSource:包含动态内容的SQL,每次执行都需要重新解析。
  • RawSqlSource:不包含动态内容的SQL(只包含#{}),可以在初始化时一次性解析。

8.3 XMLScriptBuilder解析动态SQL

XMLScriptBuilder负责解析动态SQL标签,创建对应的SqlNode对象。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class XMLScriptBuilder extends BaseBuilder {

public SqlSource parseScriptNode() {
// 解析动态标签,创建SqlNode树
MixedSqlNode rootSqlNode = parseDynamicTags(context);
SqlSource sqlSource = null;
if (isDynamic) {
// 动态SQL,使用DynamicSqlSource
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
// 静态SQL,使用RawSqlSource
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}

protected MixedSqlNode parseDynamicTags(XNode node) {
List<SqlNode> contents = new ArrayList<SqlNode>();
NodeList children = node.getNode().getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
XNode child = node.newXNode(children.item(i));
if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
// 文本节点
String data = child.getStringBody("");
TextSqlNode textSqlNode = new TextSqlNode(data);
if (textSqlNode.isDynamic()) {
// 包含${},是动态的
contents.add(textSqlNode);
isDynamic = true;
} else {
// 纯文本,可能是静态的
contents.add(new StaticTextSqlNode(data));
}
} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) {
// 元素节点(如<if>、<where>等)
String nodeName = child.getNode().getNodeName();
// 根据标签名创建对应的SqlNode处理器
NodeHandler handler = nodeHandlers(nodeName);
handler.handleNode(child, contents);
isDynamic = true;
}
}
return new MixedSqlNode(contents);
}
}

8.4 SqlNode组合模式

Mybatis使用组合模式来管理动态SQL标签。每个标签都对应一个实现了SqlNode接口的类:

  • IfSqlNode:对应标签
  • WhereSqlNode:对应标签
  • SetSqlNode:对应标签
  • ForEachSqlNode:对应标签
  • TrimSqlNode:对应标签
  • ChooseSqlNode:对应标签

这些SqlNode形成树形结构,通过apply()方法递归生成SQL片段。

8.5 DynamicSqlSource获取BoundSql

当执行动态SQL时,DynamicSqlSource会重新解析SQL:

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
public class DynamicSqlSource implements SqlSource {
private final Configuration configuration;
private final SqlNode rootSqlNode;

@Override
public BoundSql getBoundSql(Object parameterObject) {
// 创建动态上下文
DynamicContext context = new DynamicContext(configuration, parameterObject);
// 递归调用所有SqlNode的apply方法,生成SQL
rootSqlNode.apply(context);

// 解析#{}占位符,创建ParameterMapping
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());

// 获取BoundSql
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
// 将动态上下文中的附加参数添加到BoundSql
for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
}
return boundSql;
}
}

8.6 SqlSourceBuilder解析#{}占位符

SqlSourceBuilder负责解析SQL中的#{}占位符,将其替换为?,并创建ParameterMapping。

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
29
30
31
32
33
34
35
36
37
38
public class SqlSourceBuilder extends BaseBuilder {

public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
// 创建参数映射处理器
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
// 创建标记解析器
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
// 解析SQL
String sql = parser.parse(originalSql);
// 返回StaticSqlSource
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}

private static class ParameterMappingTokenHandler implements TokenHandler {
private List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
private Class<?> parameterType;
private Map<String, Object> additionalParameters;

@Override
public String handleToken(String content) {
// 构建ParameterMapping
parameterMappings.add(buildParameterMapping(content));
return "?";
}

private ParameterMapping buildParameterMapping(String content) {
// 解析参数属性(如#{name,javaType=String,jdbcType=VARCHAR})
Map<String, String> propertiesMap = parseParameterMapping(content);
String property = propertiesMap.get("property");
Class<?> propertyType = parameterType;
// ... 解析类型等信息
// 创建ParameterMapping
ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
// 设置各种属性
return builder.build();
}
}
}

九、插件实现原理源码解析

9.1 插件体系结构

Mybatis的插件机制允许拦截四大核心对象的方法调用:

  • Executor:拦截update、query、flushStatements、commit、rollback等方法
  • StatementHandler:拦截prepare、parameterize、batch、update、query等方法
  • ParameterHandler:拦截getParameterObject、setParameters等方法
  • ResultSetHandler:拦截handleResultSets、handleOutputParameters等方法

9.2 Interceptor接口

自定义插件需要实现Interceptor接口:

1
2
3
4
5
6
7
8
9
10
public interface Interceptor {
// 拦截方法
Object intercept(Invocation invocation) throws Throwable;

// 包装目标对象
Object plugin(Object target);

// 设置属性
void setProperties(Properties properties);
}

9.3 插件加载过程

在初始化阶段,XMLConfigBuilder解析plugins标签,创建Interceptor并添加到Configuration的interceptorChain中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
// 创建Interceptor实例
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
// 设置属性
interceptorInstance.setProperties(properties);
// 添加到拦截器链
configuration.addInterceptor(interceptorInstance);
}
}
}

9.4 代理链的生成

当创建Executor、StatementHandler、ParameterHandler、ResultSetHandler时,会调用InterceptorChain.pluginAll()方法,为这些核心对象生成代理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();

public Object pluginAll(Object target) {
// 逐个应用拦截器
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}

public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
}

Interceptor接口的plugin()方法通常使用Plugin.wrap()方法来生成代理对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static Object wrap(Object target, Interceptor interceptor) {
// 获取拦截器上的@Intercepts注解
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
// 创建代理对象
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}

9.5 Plugin的invoke方法

Plugin实现了InvocationHandler接口,在invoke方法中判断是否拦截该方法。

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
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;

private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 获取被代理对象的接口中需要被拦截的方法
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
// 调用拦截器的intercept方法
return interceptor.intercept(new Invocation(target, method, args));
}
// 不拦截的方法直接调用
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
}

十、缓存实现原理源码解析

10.1 一级缓存

一级缓存是SqlSession级别的缓存,默认开启。它存储在BaseExecutor的localCache属性中,是一个PerpetualCache对象。

1
2
3
4
5
6
7
8
9
public abstract class BaseExecutor implements Executor {
protected PerpetualCache localCache; // 一级缓存

// PerpetualCache本质上是一个HashMap
public class PerpetualCache implements Cache {
private final String id;
private Map<Object, Object> cache = new HashMap<Object, Object>();
}
}

一级缓存的生命周期与SqlSession相同。当执行commit、rollback、close或update操作时,会清空一级缓存。

10.2 二级缓存

二级缓存是Mapper级别的缓存,多个SqlSession共享。它使用装饰器模式对Cache接口进行功能增强。

1
2
3
4
5
6
7
8
9
public interface Cache {
String getId();
void putObject(Object key, Object value);
Object getObject(Object key);
Object removeObject(Object key);
void clear();
int getSize();
ReadWriteLock getReadWriteLock();
}

PerpetualCache是Cache的基本实现,其他缓存装饰器在其基础上添加功能:

  • LruCache:最近最少使用算法的缓存装饰器
  • FifoCache:先进先出算法的缓存装饰器
  • SoftCache:软引用缓存装饰器
  • WeakCache:弱引用缓存装饰器
  • ScheduledCache:定时清空缓存装饰器
  • BlockingCache:阻塞缓存装饰器,保证只有一个线程查询数据库

10.3 二级缓存的初始化

在解析Mapper文件时,cacheElement()方法会创建缓存对象。

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
29
30
31
private void cacheElement(XNode context) throws Exception {
if (context != null) {
// 获取缓存类型(默认为PERPETUAL)
String type = context.getStringAttribute("type", "PERPETUAL");
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
// 获取淘汰算法(默认为LRU)
String eviction = context.getStringAttribute("eviction", "LRU");
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
// 获取刷新间隔
Long flushInterval = context.getLongAttribute("flushInterval");
// 获取缓存大小
Integer size = context.getIntAttribute("size");
// 是否只读
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
// 是否阻塞
boolean blocking = context.getBooleanAttribute("blocking", false);

// 使用建造者模式创建缓存
Cache cache = new CacheBuilder(currentNamespace)
.implementation(typeClass)
.addDecorator(evictionClass)
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
configuration.addCache(cache);
currentCache = cache;
}
}

CacheBuilder的build()方法会按照顺序包装缓存装饰器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public Cache build() {
setDefaultImplementations();
// 创建基础缓存
Cache cache = newBaseCacheInstance(implementation, id);
// 设置属性
setCacheProperties(cache);
// 如果是PerpetualCache,可以添加装饰器
if (PerpetualCache.class.equals(cache.getClass())) {
for (Class<? extends Cache> decorator : decorators) {
// 包装装饰器
cache = newCacheDecoratorInstance(decorator, cache);
setCacheProperties(cache);
}
// 添加标准的装饰器:SerializedCache、LoggingCache、SynchronizedCache
cache = setStandardDecorators(cache);
} else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
// 非标准缓存,也要添加日志和同步功能
cache = new LoggingCache(cache);
cache = new SynchronizedCache(cache);
}
return cache;
}

10.4 二级缓存的使用

CachingExecutor在执行查询时,会先从二级缓存中获取数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
// 如果需要清空缓存
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
// 从缓存获取
List<E> cachedList = (List<E>) cache.getObject(key);
if (cachedList == null) {
// 缓存未命中,查询数据库
cachedList = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
// 存入缓存
cache.putObject(key, cachedList);
}
return cachedList;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

10.5 缓存Key的生成

CacheKey用于唯一标识一次查询。它由多个影响查询结果的因素组成。

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
29
30
31
32
33
34
35
36
37
38
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
// MappedStatement的ID
cacheKey.update(ms.getId());
// 分页参数
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
// SQL语句
cacheKey.update(boundSql.getSql());
// 参数值
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
cacheKey.update(value);
}
}
// 环境ID
if (configuration.getEnvironment() != null) {
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}

CacheKey的update()方法会计算哈希值,并将对象加入对象列表:

1
2
3
4
5
6
7
8
9
public void update(Object object) {
int baseHashCode = object == null ? 1 : object.hashCode();
count++;
checksum += baseHashCode;
baseHashCode *= count;
hashcode = multiplier * hashcode + baseHashCode;

updateList.add(object);
}

当比较两个CacheKey是否相等时,会比较哈希值、校验和、计数以及每个对象的值。

十一、Mybatis事务处理机制源码解析

​ 事务管理是数据库操作的核心特性之一,Mybatis提供了灵活的事务管理机制,既支持直接使用JDBC的事务,也支持托管给外部容器(如Spring)管理。

11.1 事务顶层接口:Transaction

Mybatis定义了org.apache.ibatis.transaction.Transaction接口,作为所有事务操作的抽象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface Transaction {
// 获取数据库连接
Connection getConnection() throws SQLException;

// 提交事务
void commit() throws SQLException;

// 回滚事务
void rollback() throws SQLException;

// 关闭连接
void close() throws SQLException;

// 获取事务超时时间
Integer getTimeout() throws SQLException;
}

该接口定义了事务的基本操作,具体的实现类负责与底层JDBC或容器交互。

11.2 事务的两种默认实现

Mybatis内置了两个Transaction实现类:JdbcTransactionManagedTransaction

11.2.1 JdbcTransaction

JdbcTransaction直接使用JDBC Connection的事务管理功能,通过Connection的commit()、rollback()和close()方法实现事务控制。它是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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public class JdbcTransaction implements Transaction {
protected Connection connection;
protected DataSource dataSource;
protected TransactionIsolationLevel level;
protected boolean autoCommit; // 是否自动提交

public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
dataSource = ds;
level = desiredLevel;
autoCommit = desiredAutoCommit;
}

@Override
public Connection getConnection() throws SQLException {
if (connection == null) {
openConnection(); // 懒加载打开连接
}
return connection;
}

protected void openConnection() throws SQLException {
connection = dataSource.getConnection();
if (level != null) {
connection.setTransactionIsolation(level.getLevel());
}
// 设置期望的自动提交状态
setDesiredAutoCommit(autoCommit);
}

protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
try {
if (connection.getAutoCommit() != desiredAutoCommit) {
connection.setAutoCommit(desiredAutoCommit);
}
} catch (SQLException e) {
throw new TransactionException("Error configuring AutoCommit. Cause: " + e, e);
}
}

@Override
public void commit() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
connection.commit();
}
}

@Override
public void rollback() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
connection.rollback();
}
}

@Override
public void close() throws SQLException {
if (connection != null) {
resetAutoCommit(); // 重置自动提交状态
connection.close();
}
}

protected void resetAutoCommit() {
try {
if (!connection.getAutoCommit()) {
connection.setAutoCommit(true);
}
} catch (SQLException e) {
// ignore
}
}
}

关键点:

  • JdbcTransaction维护一个数据库连接,该连接从数据源获取。
  • 它允许设置事务隔离级别和自动提交模式。
  • commit()rollback()只有在连接非自动提交状态下才真正执行。
  • close()方法会重置自动提交并关闭连接。
11.2.2 ManagedTransaction

ManagedTransaction将事务管理委托给外部容器(如Spring、Java EE容器)。它本身不执行任何事务操作,commit()和rollback()均为空实现。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class ManagedTransaction implements Transaction {
private DataSource dataSource;
private TransactionIsolationLevel level;
private Connection connection;
private final boolean closeConnection; // 是否关闭连接

public ManagedTransaction(Connection connection, boolean closeConnection) {
this.connection = connection;
this.closeConnection = closeConnection;
}

public ManagedTransaction(DataSource ds, TransactionIsolationLevel level, boolean closeConnection) {
this.dataSource = ds;
this.level = level;
this.closeConnection = closeConnection;
}

@Override
public Connection getConnection() throws SQLException {
if (this.connection == null) {
openConnection();
}
return this.connection;
}

private void openConnection() throws SQLException {
this.connection = this.dataSource.getConnection();
if (this.level != null) {
this.connection.setTransactionIsolation(this.level.getLevel());
}
}

@Override
public void commit() throws SQLException {
// 托管事务,commit不做任何事
}

@Override
public void rollback() throws SQLException {
// 托管事务,rollback不做任何事
}

@Override
public void close() throws SQLException {
if (this.closeConnection && this.connection != null) {
this.connection.close();
}
}
}

ManagedTransaction中,事务的提交和回滚由容器负责,Mybatis不干预。通常与Spring集成时会使用SpringManagedTransaction(稍后介绍)。

11.3 事务工厂 TransactionFactory

为了创建Transaction实例,Mybatis提供了TransactionFactory接口。

1
2
3
4
5
6
7
8
9
10
public interface TransactionFactory {
// 设置属性,在解析配置时调用
default void setProperties(Properties props) {}

// 根据Connection创建事务(一般用于已存在连接的情况)
Transaction newTransaction(Connection conn);

// 根据DataSource创建事务(最常见)
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}

两个内置实现类:

  • JdbcTransactionFactory:创建JdbcTransaction实例。
  • ManagedTransactionFactory:创建ManagedTransaction实例。

Configuration初始化时,会根据<environment>配置中的<transactionManager>标签决定使用哪个事务工厂。

1
2
3
4
5
6
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">...</dataSource>
</environment>
</environments>

XML解析时,type属性值会被映射到对应的事务工厂别名(JDBC→JdbcTransactionFactory,MANAGED→ManagedTransactionFactory)。

11.4 Environment 与事务的创建

Environment是Mybatis环境配置类,它聚合了DataSourceTransactionFactory

1
2
3
4
5
6
public final class Environment {
private final String id;
private final TransactionFactory transactionFactory;
private final DataSource dataSource;
// 构造函数、getter...
}

当我们通过SqlSessionFactoryBuilder构建SqlSessionFactory时,Configuration对象中持有一个Environment实例。

1
2
3
4
public class Configuration {
protected Environment environment;
// ...
}
8.4.1 创建事务的时机

当调用SqlSessionFactory.openSession()时,会根据当前环境创建事务和Executor。

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
29
30
31
32
33
34
35
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;

@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获取环境
final Environment environment = configuration.getEnvironment();
// 从事务工厂创建事务
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 创建Executor(事务传入)
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
// 异常处理,关闭事务
closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
if (environment == null || environment.getTransactionFactory() == null) {
return new ManagedTransactionFactory(); // 默认?其实不应该出现
}
return environment.getTransactionFactory();
}
}

关键点:

  • 从事务工厂创建事务时,传入DataSource、隔离级别和自动提交标志。
  • JdbcTransaction会根据autoCommit参数设置连接的初始自动提交状态。
  • Executor的构造器会接收事务对象,后续所有数据库操作都通过该事务获取连接和管理事务。

11.5 Executor 与事务的协作

Executor是Mybatis的执行器,它持有事务对象,并将事务操作委托给事务。

11.5.1 BaseExecutor 中的事务引用
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public abstract class BaseExecutor implements Executor {
protected Transaction transaction; // 事务对象
protected Executor wrapper;
protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
protected PerpetualCache localCache;
// ...

protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.configuration = configuration;
this.wrapper = this;
}

@Override
public void commit(boolean required) throws SQLException {
if (closed) {
throw new ExecutorException("Cannot commit, transaction is already closed");
}
clearLocalCache(); // 提交时清空一级缓存
flushStatements();
if (required) {
transaction.commit(); // 委托给事务提交
}
}

@Override
public void rollback(boolean required) throws SQLException {
if (!closed) {
try {
clearLocalCache();
flushStatements(true);
} finally {
if (required) {
transaction.rollback(); // 委托给事务回滚
}
}
}
}

@Override
public void close(boolean forceRollback) {
try {
try {
rollback(forceRollback);
} finally {
if (transaction != null) {
transaction.close(); // 关闭事务(即连接)
}
}
} catch (SQLException e) {
// 忽略异常
} finally {
transaction = null;
deferredLoads = null;
localCache = null;
closed = true;
}
}

// 获取连接时,通过事务获取
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
}

BaseExecutor中所有涉及事务的方法(commit、rollback、close、getConnection)都直接调用transaction对象的对应方法。因此,事务的具体行为完全由Transaction的实现类决定。

11.5.2 事务提交与回滚的传递

当我们调用SqlSession.commit()时,最终会调用到Executor.commit(true),然后触发transaction.commit()。对于JdbcTransaction,就是调用connection.commit();对于ManagedTransaction,则什么都不做。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// DefaultSqlSession
@Override
public void commit() {
commit(false);
}

@Override
public void commit(boolean force) {
try {
executor.commit(isCommitOrRollbackRequired(force));
dirty = false;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

其中isCommitOrRollbackRequired(force)判断是否需要提交事务,如果force为true或者dirty标志为true(表示有更新操作),则提交。

11.6 事务与一级缓存的关系

一级缓存(localCache)的生命周期与SqlSession绑定,而事务的提交和回滚会影响缓存的状态。

  • 提交时BaseExecutor.commit()会调用clearLocalCache()清空一级缓存。这是因为事务提交后,数据可能发生变化,之前的缓存可能失效。
  • 回滚时BaseExecutor.rollback()同样会清空一级缓存。
  • 关闭时BaseExecutor.close()也会清空缓存。
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
public abstract class BaseExecutor implements Executor {
@Override
public void commit(boolean required) throws SQLException {
if (closed) {
throw new ExecutorException("Cannot commit, transaction is already closed");
}
clearLocalCache(); // 提交时清空一级缓存
flushStatements();
if (required) {
transaction.commit();
}
}

@Override
public void rollback(boolean required) throws SQLException {
if (!closed) {
try {
clearLocalCache();
flushStatements(true);
} finally {
if (required) {
transaction.rollback();
}
}
}
}
}

这种设计保证了事务结束后,下一次查询不会读到过期的缓存数据。

十二、Spring集成源码解析

12.1 SqlSessionFactoryBean

在Spring环境中,我们通常使用SqlSessionFactoryBean来创建SqlSessionFactory。它实现了FactoryBean和InitializingBean接口。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean {

@Override
public void afterPropertiesSet() throws Exception {
// 属性检查
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");

// 创建SqlSessionFactory
this.sqlSessionFactory = buildSqlSessionFactory();
}

@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}

protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration;
XMLConfigBuilder xmlConfigBuilder = null;

// 如果有configLocation,加载配置文件
if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
configuration = new Configuration();
}

// 设置各种属性
// ...

// 解析mapper
if (this.mapperLocations != null) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} finally {
ErrorContext.instance().reset();
}
}
}

return this.sqlSessionFactoryBuilder.build(configuration);
}
}

12.2 MapperFactoryBean与@MapperScan

当我们使用@MapperScan注解时,它会导入MapperScannerRegistrar,注册ClassPathMapperScanner,将Mapper接口的BeanDefinition替换为MapperFactoryBean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
private boolean addToConfig = true;

@Override
protected void checkDaoConfig() {
// 将Mapper接口注册到Configuration
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
}

@Override
public T getObject() throws Exception {
// 获取Mapper代理对象
return getSqlSession().getMapper(this.mapperInterface);
}
}

12.3 MapperScannerConfigurer

在XML配置方式中,可以使用MapperScannerConfigurer来扫描Mapper接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean {

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 创建扫描器
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setResourceLoader(this.applicationContext);
// 注册过滤器
scanner.registerFilters();
// 扫描包
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
}

ClassPathMapperScanner扫描后会将BeanDefinition的beanClass改为MapperFactoryBean:

1
2
3
4
5
6
7
8
9
10
11
12
13
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
for (BeanDefinitionHolder holder : beanDefinitions) {
GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
// 获取原始bean类(即Mapper接口)
String beanClassName = definition.getBeanClassName();

// 将beanClass改为MapperFactoryBean
definition.setBeanClass(MapperFactoryBean.class);

// 添加构造函数参数值,即Mapper接口
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
}
}

12.4 SqlSessionTemplate与SqlSessionInterceptor

在Spring中,我们使用SqlSessionTemplate作为SqlSession的实现。它内部使用SqlSessionInterceptor来动态代理SqlSession方法,确保每个方法都在Spring事务中正确执行

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class SqlSessionTemplate implements SqlSession, DisposableBean {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy; // 代理对象

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
// 创建代理
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}

private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取当前Spring事务中的SqlSession
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
// 执行方法
Object result = method.invoke(sqlSession, args);
// 提交事务
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
// 异常处理
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// 转换异常
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
// 关闭SqlSession
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}

12.5 Spring集成中的事务处理:SpringManagedTransaction

在Spring与Mybatis集成时,为了利用Spring的事务管理,Mybatis提供了SpringManagedTransaction类(位于mybatis-spring模块中)。它实现了Transaction接口,但与Spring的事务同步机制紧密结合。

12.5.1 SpringManagedTransaction 源码分析
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class SpringManagedTransaction implements Transaction {
private final DataSource dataSource;
private Connection connection;
private boolean isConnectionTransactional;
private boolean autoCommit;

public SpringManagedTransaction(DataSource dataSource) {
this.dataSource = dataSource;
}

@Override
public Connection getConnection() throws SQLException {
if (this.connection == null) {
openConnection();
}
return this.connection;
}

private void openConnection() throws SQLException {
// 从Spring事务同步管理器获取连接
this.connection = DataSourceUtils.getConnection(this.dataSource);
this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);

// 记录初始自动提交状态
this.autoCommit = this.connection.getAutoCommit();
}

@Override
public void commit() throws SQLException {
if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
// 非事务性连接且非自动提交,才手动提交
this.connection.commit();
}
}

@Override
public void rollback() throws SQLException {
if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
this.connection.rollback();
}
}

@Override
public void close() throws SQLException {
if (this.connection != null) {
if (this.isConnectionTransactional) {
// 如果是Spring事务管理的连接,不关闭,由Spring管理生命周期
DataSourceUtils.doReleaseConnection(this.connection, this.dataSource);
} else {
// 非事务性连接,直接关闭
this.connection.close();
}
}
}
}

关键点:

  • getConnection()通过DataSourceUtils.getConnection(dataSource)获取连接,该方法会从当前Spring事务上下文中获取绑定的连接(如果有),否则从数据源获取新连接,并注册事务同步。
  • isConnectionTransactional标识当前连接是否处于Spring事务管理下。
  • commit()rollback()仅在连接非事务性且非自动提交时才执行真正的提交/回滚;否则说明事务由Spring管理,Mybatis不操作。
  • close()如果是事务性连接,则通过DataSourceUtils.doReleaseConnection()释放(而非关闭),这会让Spring事务同步管理器在事务结束时统一处理连接的关闭。
12.5.2 在SqlSessionTemplate中的应用

SqlSessionTemplate是Mybatis-Spring提供的线程安全的SqlSession实现,它内部使用动态代理,在每次方法调用时从Spring事务中获取合适的SqlSession。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 从Spring事务中获取SqlSession(可能为当前事务绑定的)
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
// 如果不是事务性的SqlSession,手动提交(根据配置)
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
// 异常处理
throw unwrapped;
} finally {
// 关闭SqlSession(实际可能放回事务同步)
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}

十三、设计模式

在上述中,我们对myBatis进行详细的解析, 我们可以看到源码中广泛应用了多种设计模式:

  • 建造者模式:SqlSessionFactoryBuilder
  • 工厂模式:SqlSessionFactory
  • 单例模式:Configuration、ErrorContext
  • 代理模式:Mapper 接口代理、Plugin
  • 装饰器模式:CachingExecutor 装饰其他 Executor
  • 模板方法模式:BaseExecutor
  • 责任链模式:插件拦截器

我们可以简单分析下这些设计模式,以及这些设计模式在mybatis源码中的使用。

13.1 建造者模式

​ 建造者模式是一种创建型设计模式,它的主要目的是将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。

13.1.1 SqlSessionFactoryBuilder

SqlSessionFactoryBuilder是建造者模式的典型应用,它负责读取配置信息并构建SqlSessionFactory。虽然它没有显式的Builder接口,但其职责符合建造者模式的定义——将复杂对象的构建过程封装起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
// ...
}
}

public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
13.1.2 XMLConfigBuilder

XMLConfigBuilder是专门用于解析mybatis-config.xml配置文件的建造者,它逐步解析各个节点,最终构建出Configuration对象。

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
public class XMLConfigBuilder extends BaseBuilder {
private boolean parsed;
private XPathParser parser;
private String environment;

public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}

private void parseConfiguration(XNode root) {
try {
propertiesElement(root.evalNode("properties"));
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
// ... 解析其他节点
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
}
13.1.3 XMLMapperBuilder

类似地,XMLMapperBuilder负责解析Mapper映射文件,构建MappedStatementResultMap等对象。

13.1.4 CacheBuilder

CacheBuilder用于构建二级缓存对象,支持链式调用设置缓存属性,最后通过build()方法创建出经过多层装饰的缓存实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
public Cache build() {
setDefaultImplementations();
Cache cache = newBaseCacheInstance(implementation, id);
setCacheProperties(cache);
if (PerpetualCache.class.equals(cache.getClass())) {
for (Class<? extends Cache> decorator : decorators) {
cache = newCacheDecoratorInstance(decorator, cache);
setCacheProperties(cache);
}
cache = setStandardDecorators(cache);
}
return cache;
}

13.2 工厂模式 (Factory Pattern)

工厂模式用于创建对象,隐藏对象创建的复杂性。Mybatis中多处使用工厂模式,尤其是SqlSessionFactoryObjectFactory

13.2.1 SqlSessionFactory

SqlSessionFactory是典型的工厂接口,它的openSession()方法创建SqlSession对象。DefaultSqlSessionFactory是具体工厂实现。

1
2
3
4
5
6
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
// ...
}
13.2.2 ObjectFactory

Mybatis提供了ObjectFactory接口,用于创建结果对象。默认实现是DefaultObjectFactory,通过反射调用构造方法创建实例。

1
2
3
4
5
public interface ObjectFactory {
<T> T create(Class<T> type);
<T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
// ...
}

当需要自定义对象创建逻辑(如使用特定构造器或工厂方法)时,可以实现该接口。

13.2.3 MapperProxyFactory

MapperProxyFactory用于为每个Mapper接口创建代理对象。它维护了方法缓存,每次调用newInstance(sqlSession)都会创建一个新的MapperProxy实例。

1
2
3
4
5
6
7
8
9
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();

public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
13.2.4 TransactionFactory

TransactionFactory是事务工厂,根据配置创建不同类型的Transaction(如JdbcTransactionManagedTransaction)。

1
2
3
4
public interface TransactionFactory {
Transaction newTransaction(Connection conn);
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}

13.3 单例模式 (Singleton Pattern)

单例模式确保一个类只有一个实例,并提供全局访问点。Mybatis中多个核心类采用单例模式。

13.3.1 Configuration

Configuration对象在整个Mybatis应用生命周期内通常只有一个实例(除非创建多个SqlSessionFactory)。它存储了所有配置信息、映射语句等,是全局唯一的配置容器。

1
2
3
public class Configuration {
// 大量配置信息...
}
13.3.2 ErrorContext

ErrorContext是线程级别的单例(实际是每个线程一个实例),用于记录错误上下文信息。它通过ThreadLocal保证每个线程拥有自己的实例。

1
2
3
4
5
6
7
8
9
10
public class ErrorContext {
private static final ThreadLocal<ErrorContext> LOCAL = ThreadLocal.withInitial(ErrorContext::new);

private ErrorContext() {
}

public static ErrorContext instance() {
return LOCAL.get();
}
}
13.3.3 LogFactory

LogFactory负责选择合适的日志实现,也是单例模式(通过静态方法返回适配器)。

13.4 代理模式 (Proxy Pattern)

代理模式为其他对象提供一种代理以控制对这个对象的访问。Mybatis在Mapper接口和插件机制中大量使用动态代理。

13.4.1 MapperProxy

MapperProxy实现了InvocationHandler,是Mapper接口的代理实现。当调用Mapper接口的方法时,会被MapperProxy拦截,转发给MapperMethod执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MapperProxy<T> implements InvocationHandler, Serializable {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
}
13.4.2 Plugin

Mybatis的插件机制使用JDK动态代理为目标对象(Executor、StatementHandler等)创建代理对象,在方法调用前后执行拦截逻辑。

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
29
30
31
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;

public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
}

13.5 责任链模式 (Chain of Responsibility Pattern)

责任链模式使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。Mybatis的插件机制本质上是责任链模式的实现。

13.5.1 InterceptorChain

InterceptorChain维护了一个拦截器列表,在创建核心对象时,通过pluginAll()方法将目标对象依次传递给每个拦截器的plugin()方法,形成代理链。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<>();

public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}

public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
}

每个拦截器可以决定是否对目标对象进行代理,以及是否将调用传递给下一个拦截器(通过Invocation.proceed())。这形成了一个责任链:请求(方法调用)沿着拦截器链传递,每个拦截器都有机会处理它。

1
2
3
4
5
6
7
8
9
public class Invocation {
private final Object target;
private final Method method;
private final Object[] args;

public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
}

13.6 策略模式 (Strategy Pattern)

策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。Mybatis在Executor、StatementHandler、TypeHandler等多个地方应用策略模式。

13.6.1 Executor类型

Mybatis提供了三种Executor类型:SIMPLE、REUSE、BATCH。它们实现了不同的Statement处理策略,但都继承自BaseExecutor

1
2
3
public enum ExecutorType {
SIMPLE, REUSE, BATCH
}

用户可以通过配置选择使用哪种Executor,这就是策略模式的典型应用——算法(执行策略)可以互相替换。

13.6.2 StatementHandler

StatementHandler也有多种实现,对应不同的JDBC Statement类型:

  • SimpleStatementHandler:使用Statement
  • PreparedStatementHandler:使用PreparedStatement
  • CallableStatementHandler:使用CallableStatement

RoutingStatementHandler中,根据MappedStatementstatementType属性动态选择具体的策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
}
13.6.3 TypeHandler

TypeHandler是类型处理器策略,负责Java类型与JDBC类型之间的相互转换。每种Java类型或JDBC类型都可以有对应的TypeHandler实现。

1
2
3
4
5
6
public interface TypeHandler<T> {
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}

Mybatis内置了大量TypeHandler,如StringTypeHandlerIntegerTypeHandler等。用户也可以自定义TypeHandler,实现特定的转换逻辑。

13.7 装饰器模式 (Decorator Pattern)

装饰器模式动态地给一个对象添加一些额外的职责。Mybatis的缓存模块大量使用装饰器模式,为基本缓存添加各种功能。

13.7.1 Cache接口

Cache是缓存组件的顶层接口,定义了基本的缓存操作。

1
2
3
4
5
6
7
8
9
10
11
public interface Cache {
String getId();
void putObject(Object key, Object value);
Object getObject(Object key);
Object removeObject(Object key);
void clear();
int getSize();
default ReadWriteLock getReadWriteLock() {
return null;
}
}
13.7.2 PerpetualCache

PerpetualCache是最基础的缓存实现,内部使用HashMap存储。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class PerpetualCache implements Cache {
private final String id;
private Map<Object, Object> cache = new HashMap<>();

@Override
public void putObject(Object key, Object value) {
cache.put(key, value);
}

@Override
public Object getObject(Object key) {
return cache.get(key);
}
// ...
}
13.7.3 缓存装饰器

Mybatis提供了多个缓存装饰器,它们都实现Cache接口,并持有一个被装饰的Cache对象:

  • LruCache:最近最少使用算法的缓存,淘汰最久未使用的元素。
  • FifoCache:先进先出算法的缓存。
  • SoftCache:基于软引用的缓存,内存不足时会被GC回收。
  • WeakCache:基于弱引用的缓存。
  • ScheduledCache:定时清空缓存。
  • BlockingCache:阻塞缓存,保证只有一个线程查询数据库。

LruCache为例:

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
29
30
31
public class LruCache implements Cache {
private final Cache delegate;
private Map<Object, Object> keyMap;
private Object eldestKey;

public LruCache(Cache delegate) {
this.delegate = delegate;
setSize(1024);
}

@Override
public void putObject(Object key, Object value) {
delegate.putObject(key, value);
cycleKeyList(key);
}

@Override
public Object getObject(Object key) {
keyMap.get(key); // 标记访问
return delegate.getObject(key);
}

private void cycleKeyList(Object key) {
keyMap.put(key, key);
if (eldestKey != null) {
delegate.removeObject(eldestKey);
eldestKey = null;
}
}
// ...
}

在创建二级缓存时,CacheBuilder会按照配置依次包装装饰器,形成功能增强的缓存链。

十四、总结

通过以上分析,我们可以看到Mybatis是一个设计精巧、层次清晰的持久层框架。它的核心流程可以概括为三个阶段:

  1. 初始化阶段:读取XML配置文件和注解中的配置信息,创建Configuration对象,并完成各个模块的初始化工作。这一阶段的核心是XMLConfigBuilder、XMLMapperBuilder等解析器,它们将配置信息解析为MappedStatement、ResultMap等对象存储在Configuration中。
  2. 代理封装阶段:为Mapper接口创建代理对象,封装了iBatis的编程模型。这一阶段的核心是MapperProxyFactory、MapperProxy和MapperMethod,它们通过JDK动态代理将接口方法调用转换为对SqlSession方法的调用。
  3. 数据访问阶段:通过SqlSession完成SQL的解析、参数的映射、SQL的执行、结果的解析过程。这一阶段的核心是Executor、StatementHandler、ParameterHandler和ResultSetHandler四大组件,它们协同完成从SQL到结果对象的完整映射。

Mybatis还通过插件机制提供了良好的扩展性,通过缓存机制提升了性能,通过与Spring的无缝集成使其成为Java应用中最流行的持久层框架之一。

理解Mybatis的源码实现,不仅可以帮助我们更好地使用这个框架,还可以学习到许多优秀的设计思想和编程技巧,如Builder模式、工厂模式、代理模式、装饰器模式、组合模式、模板方法模式等设计模式的灵活运用。


从源码深入理解Mybatis
https://johnjoyjzw.github.io/2022/12/18/从源码深入理解mybatis/
Author
JiangZW
Posted on
December 18, 2022
Licensed under