part2-sqlsession-executor
约 4616 字大约 15 分钟
2026-05-24
系列文章:《深入Spring事务与数据源:从注解到数据库连接的全链路解析》
系列三第2篇:SqlSession与Executor执行器创建完整源码解析
基于版本:MyBatis 3.5.x、MyBatis-Spring 2.1.x、Spring Framework 5.3.x
前置阅读:系列三第1篇:MyBatis自动装配与SqlSessionFactory创建
前言
上一篇文章咱们把SqlSessionFactory的创建流程搞清楚了,从自动装配到Mapper XML解析,从Configuration构建到MapperProxyFactory注册,整个启动过程都扒了个遍。
但是,SqlSessionFactory创建完成后,咱们还没看到最关键的部分——怎么执行SQL?
你可能每天都在用:
User user = userMapper.selectById(1);但是,这行代码背后发生了什么?
- SqlSession是怎么创建的?
- Executor执行器是怎么工作的?
- SimpleExecutor、ReuseExecutor、BatchExecutor有什么区别?
- MyBatis是怎么和Spring事务整合的?
别急,今天这篇文章就来回答这些问题。而且我敢说,搞懂了SqlSession和Executor,你对MyBatis的理解就能超过80%的Java开发!
好了,坐稳了,咱们开始!
一、回顾一下:Spring Boot是怎么整合MyBatis的?
老规矩,先看看自动装配的入口。不过这次咱们关注的是Mapper接口的代理创建。
核心入口1:MapperScannerConfigurer
文件位置:mybatis-spring/MapperScannerConfigurer.java
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor {
private String basePackage; // Mapper接口所在的包
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 扫描指定包下的所有Mapper接口
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
}这个类的作用是扫描Mapper接口,并注册到Spring容器。
核心入口2:MapperFactoryBean
文件位置:mybatis-spring/MapperFactoryBean.java
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
@Override
public T getObject() throws Exception {
// ★★★★★ 核心:从SqlSession获取Mapper代理对象 ★★★★★
return getSqlSession().getMapper(this.mapperInterface);
}
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
}
}看到了吧?MapperFactoryBean也是一个FactoryBean,它的getObject()方法调用sqlSession.getMapper()。
扩展场景1:如何配置Mapper扫描?
@Configuration
@MapperScan("com.example.mapper") // 扫描Mapper接口
public class MyBatisConfig {
// 配置
}或者使用注解:
@SpringBootApplication
@MapperScan("com.example.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}好了,回顾完毕,正式进入咱们的主题!
二、SqlSession接口:MyBatis的核心API
在深入源码之前,咱们得先搞清楚——SqlSession是啥?
核心接口1:SqlSession
文件位置:mybatis-3/SqlSession.java
package org.apache.ibatis.session;
import java.io.Closeable;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
/**
* SqlSession
* 这是MyBatis的核心接口
* 代表一次数据库会话
*
* 主要职责:
* 1. 执行SQL语句
* 2. 获取Mapper代理对象
* 3. 事务管理(提交、回滚)
* 4. 缓存管理
*/
public interface SqlSession extends Closeable {
// ========== 执行SQL ==========
/**
* 查询单个对象
*/
<T> T selectOne(String statement);
<T> T selectOne(String statement, Object parameter);
/**
* 查询列表
*/
<E> List<E> selectList(String statement);
<E> List<E> selectList(String statement, Object parameter);
/**
* 插入
*/
int insert(String statement);
int insert(String statement, Object parameter);
/**
* 更新
*/
int update(String statement);
int update(String statement, Object parameter);
/**
* 删除
*/
int delete(String statement);
int delete(String statement, Object parameter);
// ========== 事务管理 ==========
/**
* 提交事务
*/
void commit();
void commit(boolean force);
/**
* 回滚事务
*/
void rollback();
void rollback(boolean force);
// ========== 获取Mapper ==========
/**
* 获取Mapper代理对象
*/
<T> T getMapper(Class<T> type);
// ========== 其他方法 ==========
/**
* 关闭会话
*/
@Override
void close();
/**
* 清理缓存
*/
void clearCache();
/**
* 获取数据库连接
*/
Connection getConnection();
}看到这个接口,你可能会说:"这么多方法,都是干啥的?"
其实就四类:
- 执行SQL:selectOne、selectList、insert、update、delete
- 事务管理:commit、rollback
- 获取Mapper:getMapper
- 其他:close、clearCache、getConnection
扩展场景2:为什么要用SqlSession,而不是直接用JDBC?
// JDBC方式
Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM user WHERE id = ?");
stmt.setInt(1, 1);
ResultSet rs = stmt.executeQuery();
User user = null;
if (rs.next()) {
user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
// ... 手动映射
}
rs.close();
stmt.close();
conn.close();
// MyBatis方式
User user = sqlSession.selectOne("com.example.UserMapper.selectById", 1);
// 或者
User user = userMapper.selectById(1);看到了吧?MyBatis帮你做了:
- 参数设置:自动设置SQL参数
- 结果映射:自动将ResultSet映射为Java对象
- 资源管理:自动关闭Connection、Statement、ResultSet
- 缓存管理:自动管理一级缓存和二级缓存
三、DefaultSqlSession:SqlSession的默认实现
好,现在来看SqlSession的默认实现——DefaultSqlSession。
核心类1:DefaultSqlSession
文件位置:mybatis-3/defaults/DefaultSqlSession.java
package org.apache.ibatis.session.defaults;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.exceptions.TooManyResultsException;
import org.apache.ibatis.executor.*;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.SqlSession;
import java.sql.Connection;
import java.util.List;
/**
* SqlSession的默认实现
* 这是最常用的SqlSession实现类
*/
public class DefaultSqlSession implements SqlSession {
// Configuration配置对象
private final Configuration configuration;
// ★★★★★ 核心:Executor执行器 ★★★★★
private final Executor executor;
// 是否自动提交
private final boolean autoCommit;
// 是否有未提交的更改
private boolean dirty;
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
@Override
public <T> T selectOne(String statement, Object parameter) {
// 调用selectList
List<T> list = this.selectList(statement, parameter);
// 如果结果超过1个,抛异常
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException(
"Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// ★★★★★ 核心:获取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();
}
}
@Override
public int insert(String statement, Object parameter) {
return update(statement, parameter);
}
@Override
public int update(String statement, Object parameter) {
try {
dirty = true; // 标记为已修改
// 获取MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
// ★★★★★ 核心:调用Executor执行更新 ★★★★★
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
@Override
public void commit(boolean force) {
try {
// ★★★★★ 核心:调用Executor提交 ★★★★★
executor.commit(isCommitOrRollbackRequired(force));
dirty = false;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
@Override
public void rollback(boolean force) {
try {
// ★★★★★ 核心:调用Executor回滚 ★★★★★
executor.rollback(isCommitOrRollbackRequired(force));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error rolling back transaction. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
@Override
public <T> T getMapper(Class<T> type) {
// ★★★★★ 核心:从Configuration获取Mapper代理对象 ★★★★★
return configuration.getMapper(type, this);
}
@Override
public void close() {
try {
// ★★★★★ 核心:关闭Executor ★★★★★
executor.close(false);
} finally {
ErrorContext.instance().reset();
}
}
}看到这个类的实现,你会发现一个规律:所有的方法都委托给Executor执行!
// 查询
executor.query(ms, parameter, rowBounds, resultHandler);
// 更新
executor.update(ms, parameter);
// 提交
executor.commit(required);
// 回滚
executor.rollback(required);
// 关闭
executor.close(forceCloseConnections);这就是经典的委托模式!SqlSession只负责接口暴露,具体的执行逻辑都交给Executor。
关键点:dirty字段的作用
private boolean dirty;
@Override
public int update(String statement, Object parameter) {
dirty = true; // 标记为已修改
return executor.update(ms, parameter);
}
@Override
public void commit(boolean force) {
executor.commit(isCommitOrRollbackRequired(force));
dirty = false; // 提交后清除标记
}dirty字段用来标记是否有未提交的更改。如果没有更改,就不需要提交。
四、Executor接口:真正的执行者
好,现在来看真正的核心——Executor接口。
核心接口2:Executor
文件位置:mybatis-3/Executor.java
package org.apache.ibatis.executor;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.sql.SQLException;
import java.util.List;
/**
* Executor执行器
* 这是MyBatis的核心执行引擎
* 负责执行SQL语句
*/
public interface Executor {
// 结果处理器(表示不处理结果)
ResultHandler NO_RESULT_HANDLER = null;
// ========== 执行SQL ==========
/**
* 执行查询
*/
int update(MappedStatement ms, Object parameter) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds,
ResultHandler resultHandler) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
// ========== 事务管理 ==========
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
// ========== 缓存管理 ==========
void clearLocalCache();
void close(boolean forceRollback);
boolean isClosed();
}Executor接口很简单,核心就两个方法:
query():执行查询update():执行更新(包括insert、update、delete)
五、三种Executor实现
MyBatis提供了三种Executor实现:
5.1 SimpleExecutor(简单执行器)
每次执行SQL都创建新的Statement对象,执行完就关闭。
特点:
- 简单、安全
- 性能较差(频繁创建Statement)
- 默认执行器
5.2 ReuseExecutor(复用执行器)
复用Statement对象,相同的SQL共用一个Statement。
特点:
- 性能较好(复用Statement)
- 需要注意并发问题
5.3 BatchExecutor(批量执行器)
批量执行SQL,适用于批量插入、更新。
特点:
- 批量操作性能极高
- 只适用于update,不适用于query
扩展场景3:如何配置Executor类型?
mybatis:
executor-type: SIMPLE # SIMPLE、REUSE、BATCH或者在代码中指定:
SqlSession session = sqlSessionFactory.openSession(ExecutorType.REUSE);六、BaseExecutor:Executor的抽象基类
好,现在来看Executor的抽象基类——BaseExecutor。
核心类2:BaseExecutor
文件位置:mybatis-3/BaseExecutor.java
package org.apache.ibatis.executor;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.datasource.pooled.PooledDataSource;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.LocalCacheScope;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
/**
* Executor的抽象基类
* 实现了Executor接口的大部分方法
* 使用了模板方法模式
*/
public abstract class BaseExecutor implements Executor {
// Configuration配置对象
protected Configuration configuration;
// ★★★★★ 核心:Transaction事务对象 ★★★★★
protected Transaction transaction;
// 包装的Executor(用于责任链)
protected Executor wrapper;
// 查询调用栈(用于嵌套查询)
protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
// ★★★★★ 核心:一级缓存 ★★★★★
protected PerpetualCache localCache;
// 本地输出参数缓存
protected PerpetualCache localOutputParameterCache;
// 关闭状态
protected boolean closed;
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds,
ResultHandler resultHandler) throws SQLException {
// ★★★★★ 核心:获取BoundSql ★★★★★
BoundSql boundSql = ms.getBoundSql(parameter);
// ★★★★★ 核心:创建缓存Key ★★★★★
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
// 调用重载的query方法
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
// 如果执行器已关闭,抛异常
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--;
}
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);
// 如果是存储过程,处理输出参数
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
@Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
// 清理本地缓存(更新操作会改变数据)
clearLocalCache();
// ★★★★★ 核心:调用子类的doUpdate方法 ★★★★★
return doUpdate(ms, parameter);
}
@Override
public void commit(boolean required) throws SQLException {
// 如果事务不是自己管理的,就提交
if (required) {
transaction.commit();
}
}
@Override
public void rollback(boolean required) throws SQLException {
// 如果事务不是自己管理的,就回滚
if (required) {
transaction.rollback();
}
}
@Override
public void close(boolean forceRollback) {
try {
try {
// 如果需要强制回滚
rollback(forceRollback);
} finally {
// 关闭事务
if (transaction != null) {
transaction.close();
}
}
} catch (SQLException e) {
// 忽略
} finally {
// 清理本地缓存
localCache = null;
localOutputParameterCache = null;
closed = true;
}
}
// ========== 抽象方法(由子类实现)==========
/**
* 执行查询(子类实现)
*/
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler,
BoundSql boundSql) throws SQLException;
/**
* 执行更新(子类实现)
*/
protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;
// ... 其他抽象方法
}看到了吧?BaseExecutor用了模板方法模式!
它实现了查询、更新、提交、回滚等通用逻辑,但是把具体的执行细节留给子类:
// 抽象方法,子类实现
protected abstract <E> List<E> doQuery(...);
protected abstract int doUpdate(...);
protected abstract List<BatchResult> doFlushStatements(...);关键点1:一级缓存
// 一级缓存
protected PerpetualCache localCache;
// 查询时先从缓存获取
list = (List<E>) localCache.getObject(key);
if (list != null) {
// 缓存命中
return list;
} else {
// 缓存未命中,从数据库查询
list = queryFromDatabase(...);
// 放入缓存
localCache.putObject(key, list);
}这就是MyBatis的一级缓存机制!同一个SqlSession内的相同查询会命中缓存。
关键点2:Transaction事务对象
protected Transaction transaction;
@Override
public void commit(boolean required) {
if (required) {
transaction.commit(); // 提交事务
}
}
@Override
public void rollback(boolean required) {
if (required) {
transaction.rollback(); // 回滚事务
}
}这里用到了Transaction对象,这个对象封装了JDBC连接的事务操作。
七、SqlSession的创建流程
好,现在来看SqlSession是怎么创建的。
核心方法1:openSession
文件位置:mybatis-3/defaults/DefaultSqlSessionFactory.java
@Override
public SqlSession openSession() {
// 调用openSessionFromDataSource
return openSessionFromDataSource(
configuration.getDefaultExecutorType(), // 默认执行器类型
null, // 默认隔离级别
false); // 不自动提交
}
/**
* 从数据源创建SqlSession
*/
private SqlSession openSessionFromDataSource(ExecutorType execType,
TransactionIsolationLevel level,
boolean autoCommit) {
Transaction tx = null;
try {
// ========== 第一步:获取环境配置 ==========
final Environment environment = configuration.getEnvironment();
// ========== 第二步:创建事务工厂 ==========
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// ========== 第三步:从事务工厂创建事务 ==========
// ★★★★★ 核心:创建Transaction对象 ★★★★★
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// ========== 第四步:创建Executor执行器 ==========
// ★★★★★ 核心:根据类型创建Executor ★★★★★
final Executor executor = configuration.newExecutor(tx, execType);
// ========== 第五步:创建DefaultSqlSession ==========
// ★★★★★ 核心:创建SqlSession ★★★★★
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
// 如果创建失败,关闭事务
closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}这个方法做了5件事:
- 获取环境配置
- 创建事务工厂
- 创建Transaction对象
- 创建Executor执行器
- 创建DefaultSqlSession
关键点:最核心的就是第3步和第4步!
八、Transaction的创建
好,现在来看Transaction是怎么创建的。
核心方法2:newTransaction
文件位置:mybatis-3/TransactionFactory.java
public interface TransactionFactory {
/**
* 创建Transaction
*/
Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit);
}这个接口由子类实现,最常用的实现是JdbcTransactionFactory。
核心类3:JdbcTransaction
文件位置:mybatis-3/JdbcTransaction.java
package org.apache.ibatis.transaction.jdbc;
import org.apache.ibatis.transaction.Transaction;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* JDBC事务
* 使用JDBC的Connection管理事务
*/
public class JdbcTransaction implements Transaction {
// 数据源
protected DataSource dataSource;
// 数据库连接
protected Connection connection;
// 隔离级别
protected TransactionIsolationLevel level;
// 是否自动提交
protected boolean autoCommit;
public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
this.dataSource = ds;
this.level = desiredLevel;
this.autoCommit = desiredAutoCommit;
}
@Override
public Connection getConnection() throws SQLException {
// 如果连接为null,从数据源获取
if (connection == null) {
// ★★★★★ 核心:从数据源获取连接 ★★★★★
openConnection();
}
return connection;
}
@Override
public void commit() throws SQLException {
// 如果连接不为null,且不自动提交
if (connection != null && !connection.getAutoCommit()) {
// ★★★★★ 核心:提交事务 ★★★★★
connection.commit();
}
}
@Override
public void rollback() throws SQLException {
// 如果连接不为null,且不自动提交
if (connection != null && !connection.getAutoCommit()) {
// ★★★★★ 核心:回滚事务 ★★★★★
connection.rollback();
}
}
@Override
public void close() throws SQLException {
// 如果连接不为null
if (connection != null) {
// 恢复自动提交
resetAutoCommit();
// ★★★★★ 核心:关闭连接(归还到连接池)★★★★★
connection.close();
}
}
/**
* 打开连接
*/
protected void openConnection() throws SQLException {
// ★★★★★ 核心:从数据源获取连接 ★★★★★
this.connection = dataSource.getConnection();
// 设置隔离级别
if (level != null) {
connection.setTransactionIsolation(level.getLevel());
}
// 设置自动提交
setDesiredAutoCommit(autoCommit);
}
}看到了吧?JdbcTransaction的核心就是封装了JDBC的Connection!
getConnection():从数据源获取连接commit():调用connection.commit()rollback():调用connection.rollback()close():调用connection.close()(归还到连接池)
扩展场景4:MyBatis是怎么和Spring事务整合的?
在Spring环境中,使用的是SpringManagedTransaction,而不是JdbcTransaction。
文件位置:mybatis-spring/SpringManagedTransaction.java
public class SpringManagedTransaction implements Transaction {
private Connection connection;
private final DataSource dataSource;
private boolean isConnectionTransactional;
@Override
public Connection getConnection() throws SQLException {
if (this.connection == null) {
// ★★★★★ 核心:从Spring的事务管理器获取连接 ★★★★★
openConnection();
}
return this.connection;
}
private void openConnection() throws SQLException {
// ★★★★★ 核心:使用DataSourceUtils获取连接 ★★★★★
// 这样获取的连接和Spring事务管理器使用的是同一个连接
this.connection = DataSourceUtils.getConnection(this.dataSource);
this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(
this.connection, this.dataSource);
}
@Override
public void commit() throws SQLException {
// 如果连接不是事务性的,才提交
if (this.connection != null && !this.isConnectionTransactional) {
this.connection.commit();
}
}
@Override
public void rollback() throws SQLException {
// 如果连接不是事务性的,才回滚
if (this.connection != null && !this.isConnectionTransactional) {
this.connection.rollback();
}
}
@Override
public void close() throws SQLException {
// ★★★★★ 核心:使用DataSourceUtils释放连接 ★★★★★
// 这样不会真的关闭连接,而是归还到连接池
DataSourceUtils.releaseConnection(this.connection, this.dataSource);
}
}看到了吗?SpringManagedTransaction使用DataSourceUtils.getConnection()获取连接,这样就能和Spring事务管理器共享同一个连接!
这就是MyBatis和Spring事务整合的关键!
九、Executor的创建
好,现在来看Executor是怎么创建的。
核心方法3:newExecutor
文件位置:mybatis-3/Configuration.java
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// 如果没有指定类型,使用默认类型
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor 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);
}
// 如果开启了二级缓存,用CachingExecutor包装
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 应用插件(拦截器)
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}这个方法做了3件事:
- 根据类型创建Executor(SimpleExecutor、ReuseExecutor、BatchExecutor)
- 如果开启二级缓存,用CachingExecutor包装
- 应用插件(拦截器)
扩展场景5:CachingExecutor是什么?
public class CachingExecutor implements Executor {
// 包装的Executor
private final Executor delegate;
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, ...) {
// 先从二级缓存获取
Cache cache = ms.getCache();
if (cache != null) {
List<E> list = (List<E>) cache.getObject(key);
if (list != null) {
return list; // 二级缓存命中
}
}
// 二级缓存未命中,调用delegate查询
return delegate.query(ms, parameter, ...);
}
}CachingExecutor是一个装饰器,负责管理二级缓存。
十、完整的SqlSession创建流程图
调用:sqlSessionFactory.openSession()
↓
┌──────────────────────────────────────────┐
│ DefaultSqlSessionFactory.openSession() │
│ │
│ → openSessionFromDataSource() │
└─────────────┬────────────────────────────┘
↓
┌──────────────────────────────────────────┐
│ openSessionFromDataSource() │
│ │
│ 1. 获取Environment │
│ 2. 创建TransactionFactory │
│ 3. 创建Transaction │
│ → JdbcTransaction │
│ → 或SpringManagedTransaction │
│ 4. 创建Executor │
│ → SimpleExecutor/ReuseExecutor/ │
│ BatchExecutor │
│ → CachingExecutor包装(如果开启) │
│ → 应用插件 │
│ 5. 创建DefaultSqlSession │
│ → new DefaultSqlSession(config, │
│ executor, │
│ autoCommit) │
└──────────────────────────────────────────┘
↓
┌──────────────────────────────────────────┐
│ DefaultSqlSession创建完成 │
│ │
│ 包含: │
│ - Configuration │
│ - Executor │
│ - Transaction │
│ - Connection(从数据源获取) │
│ - autoCommit标记 │
└──────────────────────────────────────────┘十一、关键问题解答
Q1:为什么同一个SqlSession内的查询会命中一级缓存?
答案:因为BaseExecutor内部有localCache,使用CacheKey缓存查询结果。
Q2:Spring事务和MyBatis事务是怎么整合的?
答案:通过SpringManagedTransaction,使用DataSourceUtils.getConnection()获取连接,和Spring事务管理器共享同一个连接。
Q3:三种Executor有什么区别?
答案:
- SimpleExecutor:每次创建新Statement,简单安全
- ReuseExecutor:复用Statement,性能较好
- BatchExecutor:批量执行,适用于批量插入/更新
Q4:什么时候会清理一级缓存?
答案:
- 执行update/insert/delete时
- 手动调用
sqlSession.clearCache() - SqlSession关闭时
十二、总结与伏笔
通过这篇文章的分析,咱们搞清楚了SqlSession和Executor的创建流程:
- SqlSession接口:定义了执行SQL、事务管理、获取Mapper等方法
- DefaultSqlSession:委托给Executor执行所有操作
- Executor接口:真正的执行引擎
- BaseExecutor:模板方法模式,实现一级缓存
- Transaction:封装JDBC连接的事务操作
- SpringManagedTransaction:与Spring事务整合的关键
- Executor创建:SimpleExecutor/ReuseExecutor/BatchExecutor + CachingExecutor
但是,咱们还有个关键问题没深入:
Executor具体是怎么执行SQL的?
- SimpleExecutor的doQuery是怎么实现的?
- StatementHandler是怎么创建Statement的?
- ParameterHandler是怎么设置参数的?
- ResultSetHandler是怎么处理结果集的?
这些问题,将在下一篇文章《BaseExecutor与三级缓存机制》中为你揭晓。到时候咱们会深入到SQL执行的完整流程,看看MyBatis是怎么封装JDBC的。
敬请期待!
参考资料
- MyBatis 3.5.x 源码
src/main/java/org/apache/ibatis/session/SqlSession.javasrc/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.javasrc/main/java/org/apache/ibatis/executor/BaseExecutor.javasrc/main/java/org/apache/ibatis/executor/SimpleExecutor.java
- MyBatis-Spring 2.1.x 源码
src/main/java/org/mybatis/spring/transaction/SpringManagedTransaction.java
- 《MyBatis技术内幕》