第4篇-SQL执行的全链路(StatementHandler)
约 4378 字大约 15 分钟
2026-05-24
三、BaseStatementHandler:StatementHandler 的抽象基类
所有的 StatementHandler 实现都继承了 BaseStatementHandler。这个类做了很多通用的工作,比如:
- 创建
ParameterHandler和ResultSetHandler - 设置 Statement 的超时时间、FetchSize
- 处理主键生成
1. BaseStatementHandler 源码(核心方法)
类路径:org.apache.ibatis.executor.statement.BaseStatementHandler
/**
* @author Clinton Begin
*/
public abstract class BaseStatementHandler implements StatementHandler {
protected final Configuration configuration;
protected final ObjectFactory objectFactory;
protected final TypeHandlerRegistry typeHandlerRegistry;
protected final ResultSetHandler resultSetHandler;
protected final ParameterHandler parameterHandler;
protected final Executor executor;
protected final MappedStatement mappedStatement;
protected final RowBounds rowBounds;
protected BoundSql boundSql;
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject,
RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
if (boundSql == null) {
// 【核心】生成主键(比如自增 ID)
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
// 【核心】创建 ParameterHandler 和 ResultSetHandler
// 这两个家伙我们后面会详细讲!
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler,
resultHandler, boundSql);
}
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
// 【核心】实例化 Statement(由子类实现,比如创建 PreparedStatement)
statement = instantiateStatement(connection);
// 【注意】设置超时时间
setStatementTimeout(statement, transactionTimeout);
// 【注意】设置 FetchSize(控制每次从数据库读取的行数)
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
/**
* 【扩展点】子类必须实现这个方法,创建具体的 Statement 对象
* 比如 PreparedStatementHandler 会调用 connection.prepareStatement(sql)
*/
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
protected void setStatementTimeout(Statement stmt, Integer transactionTimeout) throws SQLException {
Integer queryTimeout = null;
if (mappedStatement.getTimeout() != null) {
queryTimeout = mappedStatement.getTimeout();
} else if (configuration.getDefaultStatementTimeout() != null) {
queryTimeout = configuration.getDefaultStatementTimeout();
}
if (queryTimeout != null) {
stmt.setQueryTimeout(queryTimeout);
}
StatementUtil.applyTransactionTimeout(stmt, queryTimeout, transactionTimeout);
}
protected void setFetchSize(Statement stmt) throws SQLException {
Integer fetchSize = mappedStatement.getFetchSize();
if (fetchSize != null) {
stmt.setFetchSize(fetchSize);
return;
}
Integer defaultFetchSize = configuration.getDefaultFetchSize();
if (defaultFetchSize != null) {
stmt.setFetchSize(defaultFetchSize);
}
}
// ... 省略其他方法 ...
}关键点:
- 构造函数:创建了
ParameterHandler和ResultSetHandler,这两个家伙负责参数设置和结果集处理 - prepare() 方法:调用
instantiateStatement()创建 Statement,然后设置超时时间和 FetchSize - instantiateStatement():抽象方法,由子类实现(模板方法模式)
四、PreparedStatementHandler:最常用的 StatementHandler
好了,现在我们来到了最激动人心的部分 —— PreparedStatementHandler 的源码!
这个类负责:
- 创建
PreparedStatement对象 - 设置 SQL 参数
- 执行 SQL(update/query)
- 处理结果集(query 时调用
ResultSetHandler)
1. PreparedStatementHandler 完整源码
类路径:org.apache.ibatis.executor.statement.PreparedStatementHandler
/**
* @author Clinton Begin
*/
public class PreparedStatementHandler extends BaseStatementHandler {
public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
/**
* 【核心】更新操作(INSERT/UPDATE/DELETE)
*/
@Override
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 【核心】执行 SQL!
ps.execute();
// 【注意】获取影响行数
int rows = ps.getUpdateCount();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
// 【核心】处理主键生成(比如自增 ID)
// 如果是 INSERT 语句,并且使用了自增主键,这里会把生成的主键设置到参数对象中
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}
@Override
public void batch(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 【注意】批处理:添加到批处理队列,不立即执行
ps.addBatch();
}
/**
* 【核心】查询操作(SELECT)
*/
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 【核心】执行 SQL!
ps.execute();
// 【核心】处理结果集!
// 这里交给了 ResultSetHandler 来处理,我们后面会详细讲
return resultSetHandler.handleResultSets(ps);
}
@Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleCursorResultSets(ps);
}
/**
* 【核心】实例化 Statement:创建 PreparedStatement 对象
*/
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
// 【注意】如果需要生成主键(比如自增 ID)
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
// 【核心】创建 PreparedStatement,并指定需要返回生成的主键
return connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
} else {
// 【核心】创建 PreparedStatement,并指定主键列名
return connection.prepareStatement(sql, keyColumnNames);
}
}
// 【注意】设置结果集类型(ResultSetType)
// 比如:ResultSet.TYPE_FORWARD_ONLY(只能向前滚动)
// ResultSet.CONCUR_READ_ONLY(只读)
if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(),
ResultSet.CONCUR_READ_ONLY);
}
}
/**
* 【核心】设置参数:调用 ParameterHandler.setParameters()
*/
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
}哇!是不是很简洁? 这就是 MyBatis 的设计哲学 —— 把复杂的事情交给专门的类去做:
- 创建 Statement →
instantiateStatement() - 设置参数 →
ParameterHandler.setParameters() - 执行 SQL →
PreparedStatement.execute() - 处理结果集 →
ResultSetHandler.handleResultSets()
现在,你已经知道了 SQL 执行的完整流程。但是,参数设置和结果集处理这两个过程还像个黑盒子一样。别着急,我们接下来就揭开它们的神秘面纱!
五、ParameterHandler:参数设置的魔术师
你还记得我们在上一篇中提到的 ? 占位符吗?比如:
INSERT INTO user(name, age) VALUES(?, ?)MyBatis 是如何把 Java 对象的属性值设置到这些 ? 占位符中的呢?答案就是 ParameterHandler!
1. ParameterHandler 接口源码
类路径:org.apache.ibatis.executor.parameter.ParameterHandler
/**
* A parameter handler sets the parameters of the {@code PreparedStatement}.
*
* @author Clinton Begin
*/
public interface ParameterHandler {
/**
* 获取参数对象(比如 User 对象)
*/
Object getParameterObject();
/**
* 【核心】设置参数到 PreparedStatement
* 就是把 Java 对象的属性值设置到 SQL 的 ? 占位符中
*/
void setParameters(PreparedStatement ps) throws SQLException;
}2. DefaultParameterHandler 源码(核心方法)
类路径:org.apache.ibatis.scripting.defaults.DefaultParameterHandler
/**
* @author Clinton Begin
* @author Eduardo Macarron
*/
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;
public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
this.mappedStatement = mappedStatement;
this.configuration = mappedStatement.getConfiguration();
this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
this.parameterObject = parameterObject;
this.boundSql = boundSql;
}
@Override
public Object getParameterObject() {
return parameterObject;
}
/**
* 【核心】设置参数到 PreparedStatement
* 这是整个参数设置过程的核心!
*/
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 【核心】获取参数映射列表(比如:#{name} -> name, #{age} -> age)
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
MetaObject metaObject = null;
// 【核心】遍历所有参数映射,逐个设置参数
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
// 【注意】只处理输入参数(OUT 参数不在这里处理)
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
// 【核心】获取参数值(从 Java 对象中获取)
if (boundSql.hasAdditionalParameter(propertyName)) {
// 【注意】如果是额外的参数(比如 <foreach> 生成的参数)
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
// 【注意】如果参数对象有对应的 TypeHandler(比如基本类型)
value = parameterObject;
} else {
// 【核心】通过反射获取属性值(比如 user.getName())
if (metaObject == null) {
metaObject = configuration.newMetaObject(parameterObject);
}
value = metaObject.getValue(propertyName);
}
// 【核心】获取 TypeHandler(类型处理器)
// TypeHandler 负责 Java 类型和 JDBC 类型之间的转换
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
// 【注意】如果值为 null,并且没有指定 JDBC 类型,使用默认的 JDBC 类型
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 【核心】设置参数到 PreparedStatement
// 比如:ps.setString(1, "张三")
// ps.setInt(2, 25)
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
}关键点:
- 参数映射:
ParameterMapping封装了#{name}这样的参数映射信息 - 获取参数值:通过
MetaObject反射获取 Java 对象的属性值 - TypeHandler:负责 Java 类型和 JDBC 类型之间的转换(比如
String->VARCHAR) - 设置参数:调用
TypeHandler.setParameter()设置参数到PreparedStatement
你可能会问:TypeHandler 是什么鬼?为什么需要它?
问得太好了! TypeHandler 是 MyBatis 的类型转换神器。比如:
- Java 的
String类型 -> JDBC 的VARCHAR类型 - Java 的
Integer类型 -> JDBC 的INTEGER类型 - Java 的
Date类型 -> JDBC 的TIMESTAMP类型
MyBatis 内置了很多 TypeHandler,比如:
StringTypeHandler:处理String类型IntegerTypeHandler:处理Integer类型DateTypeHandler:处理Date类型
【扩展点】 如果你想自定义类型转换逻辑(比如把 Java 的 List 转换成数据库的 VARCHAR,用逗号分隔),可以自定义 TypeHandler!
六、ResultSetHandler:结果集转换的艺术家
SQL 执行完了,数据库返回了 ResultSet。但是,ResultSet 是 JDBC 的东西,我们想要的可是 Java 对象啊!
这时候,ResultSetHandler 就闪亮登场了!它负责把 ResultSet 转换成 Java 对象(比如 User、List<User>)。
1. ResultSetHandler 接口源码
类路径:org.apache.ibatis.executor.resultset.ResultSetHandler
/**
* @author Clinton Begin
*/
public interface ResultSetHandler {
/**
* 【核心】处理结果集,转换成 Java 对象
* 比如:把 ResultSet 转换成 List<User>
*/
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
/**
* 处理游标结果集(流式查询)
*/
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
/**
* 处理输出参数(存储过程用)
*/
void handleOutputParameters(CallableStatement cs) throws SQLException;
}2. DefaultResultSetHandler 源码(核心方法)
类路径:org.apache.ibatis.executor.resultset.DefaultResultSetHandler
这个类非常复杂(有 1267 行!),但是核心逻辑很清晰。我们来看几个关键方法:
public class DefaultResultSetHandler implements ResultSetHandler {
private final Executor executor;
private final Configuration configuration;
private final MappedStatement mappedStatement;
private final RowBounds rowBounds;
private final ParameterHandler parameterHandler;
private final ResultHandler<?> resultHandler;
private final BoundSql boundSql;
private final TypeHandlerRegistry typeHandlerRegistry;
private final ObjectFactory objectFactory;
private final ReflectorFactory reflectorFactory;
// ... 省略构造函数 ...
/**
* 【核心】处理结果集,转换成 Java 对象
* 这是整个结果集处理过程的核心!
*/
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
// 【核心】获取第一个 ResultSet(存储过程可能返回多个结果集)
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
// 【核心】遍历所有结果集(存储过程可能返回多个结果集)
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 【核心】处理单个结果集
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
// ... 省略处理多个结果集的逻辑 ...
return collapseSingleResultList(multipleResults);
}
/**
* 【核心】处理单个结果集
*/
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults,
ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else if (resultHandler == null) {
// 【核心】创建 DefaultResultHandler,用于处理每一行数据
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
/**
* 【核心】处理行数据(根据 ResultMap 的类型,选择不同的处理方式)
*/
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler,
RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {
// 【注意】如果有嵌套的 ResultMap(比如一对多关系)
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
// 【核心】简单 ResultMap(没有嵌套关系)
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
/**
* 【核心】处理简单 ResultMap 的行数据
*/
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap,
ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
ResultSet resultSet = rsw.getResultSet();
// 【注意】跳过指定的行数(RowBounds 实现分页)
skipRows(resultSet, rowBounds);
// 【核心】遍历 ResultSet 的每一行
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
// 【注意】处理鉴别器(Discriminator),根据条件选择不同的 ResultMap
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
// 【核心】获取行值(把一行数据转换成 Java 对象)
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
// 【核心】存储对象(添加到结果列表中)
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
/**
* 【核心】获取行值(把一行数据转换成 Java 对象)
* 这是整个结果集转换过程的核心!
*/
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
// 【核心】自动映射(比如数据库列名 user_name -> Java 属性名 userName)
if (shouldApplyAutomaticMappings(resultMap, false)) {
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
// 【核心】属性映射(根据 ResultMap 的配置,设置属性值)
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
// ... 省略其他方法 ...
}哇! 是不是感觉 DefaultResultSetHandler 非常复杂?没错,它就是 MyBatis 中最复杂的类之一!
关键点:
- handleResultSets():入口方法,处理所有结果集
- handleRowValues():处理每一行数据
- getRowValue():把一行数据转换成 Java 对象
- 自动映射:数据库列名
user_name-> Java 属性名userName(如果开启了mapUnderscoreToCamelCase) - 属性映射:根据
ResultMap的配置,设置属性值
你可能会问:MyBatis 是如何把数据库列名 user_name 映射到 Java 属性名 userName 的?
问得好! 这就是自动映射的功劳!MyBatis 会:
- 读取
ResultSetMetaData,获取所有列名(比如user_name) - 根据
mapUnderscoreToCamelCase配置,把列名转换成属性名(比如user_name->userName) - 通过反射,调用
setter方法设置属性值(比如user.setUserName("张三"))
七、SQL 执行的全链路梳理
好了,现在我们已经把 StatementHandler、ParameterHandler、ResultSetHandler 三大核心组件都讲完了。
让我们来梳理一下 SQL 执行的全链路:
1. 更新操作(INSERT/UPDATE/DELETE)的全链路
Executor.update()
↓
SimpleExecutor.doUpdate()
↓
【1】StatementHandler.prepare(connection, transactionTimeout)
↓
BaseStatementHandler.prepare()
↓
PreparedStatementHandler.instantiateStatement(connection)
↓
connection.prepareStatement(sql) // 【核心】创建 PreparedStatement
↓
setStatementTimeout() // 设置超时时间
setFetchSize() // 设置 FetchSize
↓
【2】StatementHandler.parameterize(statement)
↓
PreparedStatementHandler.parameterize()
↓
DefaultParameterHandler.setParameters(ps)
↓
遍历 ParameterMapping
↓
TypeHandler.setParameter(ps, i+1, value, jdbcType) // 【核心】设置参数
↓
【3】StatementHandler.update(statement)
↓
PreparedStatementHandler.update()
↓
ps.execute() // 【核心】执行 SQL
↓
ps.getUpdateCount() // 获取影响行数
↓
keyGenerator.processAfter() // 处理主键生成
↓
返回影响行数2. 查询操作(SELECT)的全链路
Executor.query()
↓
SimpleExecutor.doQuery()
↓
【1】StatementHandler.prepare(connection, transactionTimeout)
↓
(同上,创建 PreparedStatement)
↓
【2】StatementHandler.parameterize(statement)
↓
(同上,设置参数)
↓
【3】StatementHandler.query(statement, resultHandler)
↓
PreparedStatementHandler.query()
↓
ps.execute() // 【核心】执行 SQL
↓
DefaultResultSetHandler.handleResultSets(ps) // 【核心】处理结果集
↓
遍历 ResultSet
↓
getRowValue() // 把一行数据转换成 Java 对象
↓
createResultObject() // 创建 Java 对象(比如 new User())
↓
applyAutomaticMappings() // 自动映射
↓
applyPropertyMappings() // 属性映射
↓
返回结果列表(比如 List<User>)
↓
返回查询结果看到了吗? 整个流程非常清晰:
- 创建 Statement
- 设置参数
- 执行 SQL
- 处理结果集
这就是 MyBatis 的 SQL 执行全链路!是不是感觉豁然开朗了?😎
八、Spring Boot 自动装配入口:MybatisAutoConfiguration
按照我们的约定,每个系列都必须包含 Spring Boot 自动装配的核心入口代码解读。
在系列三中,我们需要解读 MybatisAutoConfiguration。但是,MybatisAutoConfiguration 主要是配置了 SqlSessionFactoryBean 和 SqlSessionTemplate,并没有直接配置 StatementHandler。
那么,StatementHandler 是在哪里创建的呢?
答案:StatementHandler 是在 Executor 中创建的!具体来说,是在 BaseExecutor 的 prepareStatement() 方法中创建的。
但是,MybatisAutoConfiguration 配置了 SqlSessionFactoryBean,而 SqlSessionFactoryBean 会创建 SqlSessionFactory,SqlSessionFactory 会创建 SqlSession,SqlSession 会创建 Executor,Executor 会创建 StatementHandler。
所以,我们还是来回顾一下 MybatisAutoConfiguration 的核心代码吧!
1. MybatisAutoConfiguration 源码(核心方法)
类路径:org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
@Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean {
// ... 省略 ...
/**
* 【核心】创建 SqlSessionFactoryBean
*/
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
// 【核心】设置数据源(就是我们在 application.yml 中配置的数据源)
factory.setDataSource(dataSource);
// 【注意】设置 MyBatis 配置(比如 typeAliasesPackage、mapperLocations)
factory.setConfiguration(properties.getConfiguration());
// ... 省略其他配置 ...
// 【核心】创建 SqlSessionFactory
return factory.getObject();
}
/**
* 【核心】创建 SqlSessionTemplate(Spring 整合的 SqlSession)
*/
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
// 【注意】可以指定执行器类型(SIMPLE/REUSE/BATCH)
return new SqlSessionTemplate(sqlSessionFactory, properties.getExecutorType());
}
// ... 省略 MapperScannerConfigurer ...
}关键点:
- sqlSessionFactory():创建
SqlSessionFactoryBean,设置数据源、MyBatis 配置等 - sqlSessionTemplate():创建
SqlSessionTemplate,指定执行器类型
你可能会问:StatementHandler 是在哪里创建的呢?
好问题! StatementHandler 是在 Executor 中创建的。具体来说,是在 BaseExecutor 的 prepareStatement() 方法中:
// 类路径:org.apache.ibatis.executor.BaseExecutor
protected Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
// 【核心】创建 Statement(调用 StatementHandler.prepare())
stmt = handler.prepare(connection, transaction.getTimeout());
return stmt;
}然后,在 SimpleExecutor.doQuery() 或 doUpdate() 中,会创建 StatementHandler:
// 类路径:org.apache.ibatis.executor.SimpleExecutor
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
// 【核心】创建 StatementHandler
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapperExecutor, ms, parameter, rowBounds, resultHandler, boundSql);
// 【核心】准备 Statement(创建 PreparedStatement、设置参数)
stmt = prepareStatement(handler, ms.getStatementLog());
// 【核心】执行查询
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}看到了吗? StatementHandler 是在 Executor.doQuery() 或 doUpdate() 中创建的!这就是 MyBatis 的执行流程。
九、扩展点:自定义 StatementHandler
【扩展点】 如果你想自定义 SQL 执行逻辑(比如添加 SQL 执行日志、监控 SQL 执行时间、修改 SQL 语句),可以自定义 StatementHandler!
1. 自定义 StatementHandler 示例
/**
* 自定义 StatementHandler:添加 SQL 执行日志
*/
public class LoggingStatementHandler extends PreparedStatementHandler {
private final StatementHandler delegate;
public LoggingStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
@Override
public int update(Statement statement) throws SQLException {
long start = System.currentTimeMillis();
try {
return super.update(statement);
} finally {
long end = System.currentTimeMillis();
System.out.println("SQL 执行时间:" + (end - start) + "ms");
}
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
long start = System.currentTimeMillis();
try {
return super.query(statement, resultHandler);
} finally {
long end = System.currentTimeMillis();
System.out.println("SQL 执行时间:" + (end - start) + "ms");
}
}
}2. 注册自定义 StatementHandler
注意:MyBatis 没有直接提供注册自定义 StatementHandler 的接口。但是,你可以通过 插件机制(Interceptor) 来实现!
/**
* 自定义插件:拦截 StatementHandler 的 update() 和 query() 方法
*/
@Intercepts({
@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class})
})
public class LoggingInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = System.currentTimeMillis();
try {
return invocation.proceed();
} finally {
long end = System.currentTimeMillis();
System.out.println("SQL 执行时间:" + (end - start) + "ms");
}
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}下一篇,我们会详细讲解 MyBatis 的插件机制与责任链,到时候你会看到更多自定义的神奇用法!
十、总结与下一篇预告
1. 总结
今天,我们深入剖析了 MyBatis 的 SQL 执行全链路,包括三大核心组件:
- StatementHandler:SQL 执行的指挥官(创建 Statement、设置参数、执行 SQL、处理结果集)
- ParameterHandler:参数设置的魔术师(把 Java 对象的属性值设置到
?占位符中) - ResultSetHandler:结果集转换的艺术家(把
ResultSet转换成 Java 对象)
我们还梳理了 SQL 执行的全链路:
- 更新操作:
Executor.update()->StatementHandler.prepare()->parameterize()->update() - 查询操作:
Executor.query()->StatementHandler.prepare()->parameterize()->query()->ResultSetHandler.handleResultSets()
2. 下一篇预告
在下一篇(系列三第5篇)中,我们会讲解 MyBatis 的插件机制与责任链。
你会学到:
- MyBatis 插件的工作原理(动态代理 + 责任链)
- 如何自定义插件(比如分页插件、SQL 监控插件)
- 插件的应用场景和注意事项
你可能会问:为什么 MyBatis 的插件这么强大?它是如何拦截 MyBatis 的核心组件的?
别着急,下一篇我们就来揭开这个秘密!相信我,这会是一次非常有趣的旅程!🚀
写到这里,我不禁感叹:MyBatis 的设计真的是太精妙了!每一个组件都职责清晰,易于扩展。这不就是我们所追求的高内聚、低耦合吗?
好了,今天的文章就到这里。如果你觉得有收获,别忘了点赞、收藏、关注哦!你的支持,是我创作的最大动力!❤️
我们下一篇见!👋