一、构建 SqlSessionFactory 的过程
SqlSessionFactory
是 MyBatis
核心类之一,其重要功能是创建 MyBatis 的核心接口 SqlSession
。MyBatis 通过 SqlSessionFactoryBuilder
构建 SqlSessionFactory
,构建分为两步:
- 通过
org.apache.ibatis.builder.xml.XMLConfigBuilder
解析 XML 配置文件,读取配置参数并存入org.apache.ibatis.session.Configuration
中 - 使用
Configuration
构建SqlSessionFactory
,MyBatis 提供了SqlSessionFactory
的默认实现类org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
这种创建方式是一种 Builder 模式。对于复杂对象而言,直接使用构造函数构建会导致大量的逻辑放在构造函数中,使得代码看起来很繁杂。使用一个参数类总领全局,然后按步骤构建可以降低构建的复杂性
1.1 构建 Configuration
Configuration
的作用如下:
- 读入配置文件,包括基础配置文件和映射器文件
- 初始化基础配置,比如 MyBatis 的别名,一些重要的类对象(插件、映射器、
ObjectFactory
和TypeHandler
) - 提供
Configuration
单例,为后续创建SqlSessionFactory
服务提供配置参数
1.2 映射器内部组成
插件需要频繁访问映射器内部组成,所以有必要单独研究一下映射器的内部组成。一般而言,映射器由 3 部分组成:
MappedStatement
:保存映射器的一个节点,包括配置的 sql、sql id、resultMap、resultType 等重要配置内容SqlSource
:提供BoundSql
的地方,是MappedStatement
的一个属性BoundSql
:建立 SQL 和相关参数的地方,有三个常用属性,sql、parameterObject 和 parameterMappings
一般而言,我们主要对参数和 SQL 进行修改,这部分主要反映在 BoundSql
类上,在插件中可以通过 BoundSql
拿到当前运行 SQL 和参数及参数规则,并做出适当修改,满足我们的需求
BoundSql
主要有三个属性:sql
、rameterObject
和 parameterMappings
parameterObject
:参数对象本身,可以传递简单对象、POJO
、Map
或者@Param
注解的参数,其传递规则如下:- 传递简单对象会将其变为简单对象的包装类传递,如
int
变为Integer
传递 - 传递
POJO
或Map
,parameterObject 就是传入的POJO
或Map
不变 - 当传递多个参数,如果没有
@Param
注解,那么 MyBatis 会将 parameterObject 变为一个Map<String, Object>
对象,类似于这样的形式:{"param1":p1, "param2":p2}
,所以我们可以使用 #{param1} 引用一个参数 - 如果使用
@Param
注解,那么 MyBatis 会将 parameterObject 变为一个Map<String, Object>
对象。只是将 Map 的键值置换为@Param
注解的键值。例如@Param("key1") String p1, @Param("key2") String p2
,parameterObject 的形式为{"key1":p1, "key2":p2}
- 传递简单对象会将其变为简单对象的包装类传递,如
parameterMappings
:是一个List
,每个元素都是ParameterMapping
对象。这个对象会描述我们的参数,参数包括属性、名称、表达式、JavaType、typeHandler 信息,一般不会去改变它。通过 parameterMappings 可以实现参数和 SQL 的结合,以便PreParedStatement
通过 parameterMappings 找到 parameterObject 对象的属性并设置参数sql
:即我们书写在映射器里面的一条 SQL,可以通过插件进行改写
1.3 构建 SqlSessionFactory
有了 Configuration
便可以快速构建 SqlSessionFactory
二、SqlSession 运行过程
2.1 映射器动态代理
todo
2.2 SqlSession 四大对象
映射器其实就是一个动态代理对象,最终会进入 MapperMethod
的 execute
方法。execute
经过简单判断就调用 SqlSession 的删除、更新、插入、选择等方法。这些方法是通过 Executor
、StatementHandler
、ParameterHandler
和 ResultHandler
来完成数据库操作和结果返回的
Executor
:执行器,负责调度StatementHandler
、ParameterHandler
和ResultHandler
执行 SQL并返回结果StatementHandler
:使用数据库的 Statement(PrepareStatement
)执行操作,是四大对象的核心ParameterHandler
:用于 SQL 对参数的处理ResultHandler
:进行最后结果数据集(ResultSet
)的封装返回处理
2.2.1 Executor
Executor
负责执行 Java 和 数据库交互,在 MyBatis 中存在三种执行器,可以在配置文件的 setting 元素的 defaultExecutorType
指定
public enum ExecutorType {
SIMPLE, REUSE, BATCH
}
- SIMPLE:简单执行器,它是默认执行器
- REUSE:重用预处理语句的执行器
- BATCH:重用语句和批量更新,针对批量专用的执行器
构造过程如下:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
执行过程中,interceptorChain.pluginAll(executor)
就是 MyBatis 插件执行的地方,这行代码会为 executor
构建一层层代理对象,在调度真实的 Executor
方法之前执行配置插件的代码可以修改。SimpleExecutor
的查询处理过程如下所示:
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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
如代码所示,SimpleExecutor
的查询过程一共有 4 步:
- 通过
configuration
创建StatementHandler
- 调用
StatementHandler#prepare
进行预编译和基础设置 - 调用
StatementHandler#parameterize
设置参数 - 调用
StatementHandler#query
执行查询并通过ResultHandler
组装查询结果并返回
2.2.2 StatementHandler
StatementHandler
,顾名思义就是处理数据库会话的,其创建过程如下: