源码分析 - MyBatis 核心接口 SqlSession 实现原理
在上一篇文章中 MyBatis 动态代理调用过程源码分析,我们知道了 MyBatis 动态代理的核心是 MapperProxy
,在它内部封装了动态代理的调用逻辑,而我们也知道了在使用动态代理进行操作的时候实际上还是调用的 SqlSession
中的 API 去实现的,那么我们今天就来分析一波 SqlSession
的源码,由于 SqlSession
中方法很多,我们就已查询方法为例进行分析。
一. 核心接口 SqlSession
SqlSession
是 MyBatis 对外提供的核心接口,通过它可以执行数据库读写命令、获取映射器、管理事务等;DefaultSqlSession
是SqlSession
接口的实现类,它是真正执行数据库操作的门面类,它内部封装着复杂的数据库操作逻辑;SqlSessionFactory
是SqlSession
的工厂类,负责创建DefaultSqlSession
实例(详见DefaultSqlSessionFactory::openSession
方法);SqlSessionManager
是对SqlSession
的一种加强,当用户调用 CRUD 方法时,会查询ThreadLocal
中 当前线程是否已经创建SqlSession
,如果没有创建则调用SqlSessionFactory
创建SqlSession
调用 对应的方法,如果当前线程已经创建过SqlSession
,则使用缓存的SqlSession
二. 天下归一 selectList
SqlSession
内部有着丰富的查询接口,他们看似实现着不同的功能,但实际上最终执行的都是 DefaultSqlSession::selectList(String, Object, RowBounds)
方法:
我们就以 selectOne
为例子:
@Override
public <T> T selectOne(String statement) {
return this.selectOne(statement, null);
}
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.selectList(statement, parameter);
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;
}
}
可以看到 selectOne
实际上还是调用的 selectList
获取的结果集,然后取出结果集中的第一个返回。
三. 项目经理 Executor
进入 DefaultSqlSession::selectList(String, Object, RowBounds)
方法我们能够发现,它调用的是 Executor::query
方法:
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//获取 Configuration 中保存的 statementId 对应的 Sql 节点信息
MappedStatement ms = configuration.getMappedStatement(statement);
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();
}
}
可以看到 SqlSession 中所有方法最终都交由了 Executor 去执行了,而 Executor 就像一个项目经理。老板(SqlSession)只需要将任务交给项目经理(Executor),任务执行的具体过程老板并不关心,它只关心经理对任务的完成情况。
Executor 继承体系结构:
3.1 一级缓存管理
我们先看 BaseExecutor
分支,它使用了典型的模板方法模式,在 BaseExecutor::query
中实现了基本的算法骨架,而真正执行查询的方法 doQuery
交由子类实现:
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//获取 SQL 信息
BoundSql boundSql = ms.getBoundSql(parameter);
//根据 statementId、SQL 语句、参数、分页信息等生产缓存的 Key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
//执行重载 query 方法
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
@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.");
}
//没有嵌套查询且 flushCache=tre 则清空缓存
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();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
//如果当前 SQL 的一级缓存配置为 STATEMENT,查询完既清空缓存
// issue #482
clearLocalCache();
}
}
return list;
}
可以看到在 BaseExecutor::query
中实现了一级缓存的基本逻辑。当一级缓存没有命中时会调用虚方法 abstract doQuery
,此时这个方法就等着子类去实现了。而在当前的 MyBatis 版本中(3.5.5) BaseExecutor
拥有如下子类:
BatchExecutor
:执行批处理操作时使用SimpleExecutor
:默认配置,使用Statement
执行 SQL 语句,每一次执行都会创建一个新的Statement
。(至于使用PreparedStatement
还是Statement
执行 SQL 语句,这取决于 SQL 标签中配置的statementType
属性,默认使用PreparedStatement
。下同)ReuseExecutor
:使用Statement
执行 SQL 语句,会重用缓存中的 Statement 对象。
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//获取 Statment,与 SimpleExecutor 中不同的是,ReuseExecutor 在这里会先去缓存中寻找 Statement,如果没有再创建
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
}
ClosedExecutor
:关闭后的执行器,内部的所有方法都是未实现的
protected <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
throw new UnsupportedOperationException("Not supported.");
}
3.2 二级缓存管理
可以看到在 BaseExecutor
主要实现了一级缓存的功能,它只有在一级缓存未命中的情况下才会去真正执行数据库查询。但是熟悉 MyBatis 的小伙伴应该知道,MyBatis 还提供了二级缓存的实现,此时我们就需要将目光转向 CachingExecutor
分支了。
CachingExcutor
实际上是一个装饰器,它封装了二级缓存的实现逻辑
/**
* Executor 的二级缓存装饰器,它是实现 MyBatis 二级缓存的关键
* @author Clinton Begin
* @author Eduardo Macarron
*/
public class CachingExecutor implements Executor {
private final Executor delegate;
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}
//........
@Override
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) {
ensureNoOutParams(ms, boundSql);
//从二级缓存中获取数据
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
//二级缓存为空,才调用被装饰的 Executor 的 query 获取数据,由于 CachingExecutor 大多数时候是用来装饰 SimpleExecutor 对象,所以 CachingExecutor
// 中的二级缓存逻辑会先执行,如果二级缓存中没有数据,才会执行 SimpleExecutor 中一级缓存的逻辑
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
//......
}
可以看到只要被 CachingExecutor
装饰过的 Executor,都具备了二级缓存的功能。
走到这里,可能有很多小伙伴对二级缓存的实现原理仍然不太清楚,那这个故事还得从 Executor
的创建说起,我们先定位到 Executor 创建的地方 DefaultSqlSessionFactory::openSession
:
public SqlSession openSession() {
//从数据源中获取连接,然后创建 SqlSessionFactory
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
openSession
方法直接调用了 openSessionFromDataSource
方法:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//获取 mybatis-config.xml 中的 enviroment 对象
final Environment environment = configuration.getEnvironment();
//从 Enviroment 获取 TranslationFactory
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//从数据源中获取数据库连接,然后创建 Transaction 对象
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//重点:根据配置创建 Executor,该方法内部会根据用户是否配置二级缓存去决定是否创建二级缓存的装饰器去装饰 Executor,这也是二级缓存是否生效的关键
final Executor executor = configuration.newExecutor(tx, execType);
//创建 DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
可以看到在 openSessionFromDataSource
方法中调用了 Configuration::newExecutor
方法,它就是 Executor
的核心逻辑。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
//创建 Batch 类型的 Executor
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
//创建 Reuse 类型的 Executor
executor = new ReuseExecutor(this, transaction);
} else {
//创建 simple 类型的 Executor
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
//如果配置了二级缓存,则用 CachingExecutor 装饰前面创建的 Executor,从而实现二级缓存
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
当用户配置二级缓存后,就会创建 CachingExecutor
装饰器去包装 SimpleExecutor
,这也是 MyBatis 二级缓存始终会优先于一级缓存的原因。
3.3 项目经理不干活
前面说了这么多,咱还是小小的总结一下吧。 SqlSession
的所有查询方法最后都会交给 selectList
来执行,而 selectList
将任务下发给了项目经理( Executor
)去执行,项目经理会先去查二级缓存有没有已经缓存的结果,如果没有则会去查一级缓存,如果还是没有命中则会去执行 doQuery
方法操作数据库。
好的,那我们把视角转回到 SimpleExecutor:doQuery
方法中:
@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();
//根据 SQL 标签中配置 statementType 来创建不同的 StatementHandler 实现
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//创建 Statement,并使用 parameterHandler 对占位符进行赋值
stmt = prepareStatement(handler, ms.getStatementLog());
//通过 statementHandler 对象调用 ResultSetHandler 将结果集转化为指定对象返回
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
doQuery
方法实际上也没有真正执行数据库操作,而是将这个艰巨任务交给了 StatementHandler
去执行了,而这样看上去确实符合项目经理的调性,自己负责分配任务,苦逼程序员负责完成需求,哈哈。
跳回到计算机的世界,这种设计思想也完美符合设计模式中的单一职责原则, Executor
只负责一二级缓存的实现,而数据库的操作交由 *Handler
实现。
四. 苦逼程序员
领导将任务下发后,就需要底下的项目组成员去完成了。而在 MyBatis 中一次操作任务会下发给三个“程序员”,它们分别负责不同的任务:
- ParameterHandler:对预编译的 SQL 语句进行参数设置,
DefualtParameterHandler
是其实现类 - StatementHandler: 执行 SQL 语句,获取 JDBC 返回的
ResultSet
, - ResultSetHandler:对数据库返回的结果集进行封装,
DefaultResultSetHandler
是其实现类
4.1 ParameterHandler
在 3.3 节中,我们分析了 SimpleExecutor:doQuery
方法,其内部调用了 SimpleExecutor::prepareStatement
方法用于获取 PreparedStatement
实例并设置占位符的参数值:
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//试图创建带有日志功能的 Connection,也就是 ConnectionLogger
Connection connection = getConnection(statementLog);
//试图创建带有日志功能 PreparedStatement,也就是 PreparedStatementLogger
stmt = handler.prepare(connection, transaction.getTimeout());
//设置参数值
handler.parameterize(stmt);
return stmt;
}
可以看到 SimpleExecutor::prepareStatement
方法调用了 Statement:parameterize
方法,我们进入 PreparedStatementHandler
一探究竟:
public void parameterize(Statement statement) throws SQLException {
//设置参数值
parameterHandler.setParameters((PreparedStatement) statement);
}
可以看到,设置参数值的操作最终是交给了 ParameterHandler
去实现:
/**
* 对预编译的 SQL 语句进行参数设置
* @author Clinton Begin
* @author Eduardo Macarron
*/
public class DefaultParameterHandler implements ParameterHandler {
//TypeHandler 注册中心
private final TypeHandlerRegistry typeHandlerRegistry;
//对应的 SQL 节点信息
private final MappedStatement mappedStatement;
//用户传入的参数
private final Object parameterObject;
//SQL 语句信息
private final BoundSql boundSql;
private final Configuration configuration;
//...
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
//从 boundSql 中获取 sql 语句的占位符对应的参数信息
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)) { // issue #448 ask first for additional params
//如果 SQL 中的参数列表中包含这个参数,则获取值
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 typeHandler = parameterMapping.getTypeHandler();
//获取参数对应的 jdbcType
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
//由 typeHandler 设置参数值
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
}
4.2 StatementHandler
MyBatis 进行数据库操作的核心是由 StatementHandler
完成的,而 StatementHandler
只是一个接口,它拥有如下几个实现类:
- BaseStatementHandler:所有子类的父类,模板方法模式,内部定义了数据库操作的操作步骤,核心功能交由子类实现。
- SimpleStatmentHandler :使用 Statement 对象访问数据库,无须参数化;
- PreparedStatmentHandler:使用预编译 PrepareStatement 对象访问数据库;
- CallableStatmentHandler :调用存储过程;
我们再次回到 SimpleExecutor:doQuery
方法中:
@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();
//根据 SQL 标签中配置 statementType 来创建不同的 StatementHandler 实现
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//创建 Statement,并使用 parameterHandler 对占位符进行赋值
stmt = prepareStatement(handler, ms.getStatementLog());
//通过 statementHandler 对象调用 ResultSetHandler 将结果集转化为指定对象返回
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
doQuery
最后调用的是 StamentHandler::query
方法,我们进入其实现类中( PreparedStatementHandler::query
)可以看到最终还是调用的 JDBC 中 PreparedStatement::execute
方法:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//执行 SQL 语句
ps.execute();
//封装结果集并返回对象
return resultSetHandler.handleResultSets(ps);
}
4.3 ResultSetHandler
ResultSetHandler
的唯一实现类 DefaultResultSetHandler
用于对返回结果集进行包装。
DefaultResultSetHandler::handleResultSets
源码:
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;
//statement 可能返回多个结果集对象,这里先取出第一个结果集
ResultSetWrapper rsw = getFirstResultSet(stmt);
//获取映射规则,对应映射配置文件中 resultMap 标签。有时候 sql 标签的 resultMap 属性会指定多个结果集。
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
//结果集和 resultMap 不能为空,为空抛出异常
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
//循环处理多个结果集
//获取当前结果集对应的 resultMap
ResultMap resultMap = resultMaps.get(resultSetCount);
//根据映射规则对结果进行转化,转换成目标对象以后放入 multipleResults
handleResultSet(rsw, resultMap, multipleResults, null);
//获取下一个结果集
rsw = getNextResultSet(stmt);
//清空 nestedResultObjects 对象
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
封装单个结果集:
/**
* 封装单个结果集数据,将结果保存至 multipleResults 中
* @param rsw
* @param resultMap
* @param multipleResults
* @param parentMapping
* @throws SQLException
*/
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) {
//如果 resultHandler 为空则,实例化一个默认的 resultHandler
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
//封装结果集数据,然后将数据暂存在 defaultResultHandler 中
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
//将暂存在 defaultResultHandler 中的数据放入 multipleResults 中
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {
//处理嵌套结果集的情况
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
//处理没有嵌套结果集的情况
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
/**
* 根据一个简单 ResultMap 规则,封装结果集数据,然后将数据暂存至 resultHandler
* @param rsw
* @param resultMap
* @param resultHandler 封装结果的暂存区
* @param rowBounds
* @param parentMapping
* @throws SQLException
*/
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
//创建结果上下文,所谓的上下文就是专门在循环中缓存结果对象的
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
ResultSet resultSet = rsw.getResultSet();
//根据分页信息,定位到指定记录
skipRows(resultSet, rowBounds);
//循环处理每一行数据
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
//进一步完善 resultMap 信息,主要处理鉴别器信息
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
//重点解读:读取 ResultSet 中的一行数据进行映射,转化并返回目标对象
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
//保存映射结果对象
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
封装单行数据:
/**
* 读取一行数据,并封装成指定类型对象中,实例封装的核心
* @param rsw
* @param resultMap
* @param columnPrefix
* @return
* @throws SQLException
*/
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())) {
//结果集不为空,且存在结果对象的类型处理程序
//封装 MetaObject 对象,方便赋值
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
if (shouldApplyAutomaticMappings(resultMap, false)) {
//进行自动映射。一般情况下 autoMappingBehavior 的默认值为 PARTIAL,对未明确指定映射规则的字段进行自动映射
//是否自动映射由 resultMap 标签中的 autoMapping 属性决定,默认值为 true
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
//进行属性映射
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
//如果发现数据或者懒加载机制中还有没有加载出来的数据
foundValues = lazyLoader.size() > 0 || foundValues;
//如果没有数据且不允许空行返回实例,则将 rowValue 置为空
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
五. 时序图
由于图片版幅较大,网页显示字体会看不清,这里给出下载链接:
六. 总结
SqlSession
作为 MyBatis 框架的核心接口隐藏了底层 Executor
接口以及 xxxHandler
接口复杂的实现逻辑。用户对 SqlSession
的操作实际上都交由 Executor
去执行,在 Executor
处理完一二级缓存逻辑后,就将数据库操作以及结果集封装的工作全部交由 ParameterHandler
、 StatementHandler
、 ResultSetHandler
去完成。
本文只是展现了 SqlSession 查询数据库框架的基本实现流程,更加细节的信息需要小伙伴们自己去挖掘。博主自己对 MyBatis 源码进行了详细注释,如有需要,请移步至: GitHub 或 Gitee
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: MyBatis 反射模块源码分析
下一篇: 谈谈自己对于 AOP 的了解
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论