源碼分析——MyBatis核心接口SqlSession的實現(xiàn)原理
在上一篇文章中(《MyBatis動態(tài)代理調(diào)用過程源碼分析》),我們知道了MyBatis動態(tài)代理的核心是MapperProxy,在它內(nèi)部封裝了動態(tài)代理的調(diào)用邏輯,而我們也知道了在使用動態(tài)代理進行操作的時候?qū)嶋H上還是調(diào)用的SqlSession中的API去實現(xiàn)的,那么我們今天就來分析一波SqlSession的源碼,由于SqlSession中方法很多,我們就已查詢方法為例進行分析。
一. 核心接口SqlSession

SqlSession是MyBatis對外提供的核心接口,通過它可以執(zhí)行數(shù)據(jù)庫讀寫命令、獲取映射器、管理事務(wù)等;DefaultSqlSession是SqlSession接口的實現(xiàn)類,它是真正執(zhí)行數(shù)據(jù)庫操作的門面類,它內(nèi)部封裝著復(fù)雜的數(shù)據(jù)庫操作邏輯;SqlSessionFactory是SqlSession的工廠類,負責(zé)創(chuàng)建DefaultSqlSession實例(詳見DefaultSqlSessionFactory::openSession方法);SqlSessionManager是對SqlSession的一種加強,當(dāng)用戶調(diào)用CRUD方法時,會查詢ThreadLocal中 當(dāng)前線程是否已經(jīng)創(chuàng)建SqlSession,如果沒有創(chuàng)建則調(diào)用SqlSessionFactory創(chuàng)建SqlSession調(diào)用 對應(yīng)的方法,如果當(dāng)前線程已經(jīng)創(chuàng)建過SqlSession,則使用緩存的SqlSession
二. 天下歸一selectList
SqlSession內(nèi)部有著豐富的查詢接口,他們看似實現(xiàn)著不同的功能,但實際上最終執(zhí)行的都是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實際上還是調(diào)用的selectList獲取的結(jié)果集,然后取出結(jié)果集中的第一個返回。
三. 項目經(jīng)理Executor
進入DefaultSqlSession::selectList(String, Object, RowBounds)方法我們能夠發(fā)現(xiàn),它調(diào)用的是Executor::query方法:
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//獲取Configuration中保存的statementId對應(yīng)的Sql節(jié)點信息
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去執(zhí)行了,而Executor就像一個項目經(jīng)理。老板(SqlSession)只需要將任務(wù)交給項目經(jīng)理(Executor),任務(wù)執(zhí)行的具體過程老板并不關(guān)心,它只關(guān)心經(jīng)理對任務(wù)的完成情況。
Executor繼承體系結(jié)構(gòu):

3.1 一級緩存管理
我們先看BaseExecutor分支,它使用了典型的模板方法模式,在BaseExecutor::query中實現(xiàn)了基本的算法骨架,而真正執(zhí)行查詢的方法doQuery交由子類實現(xiàn):
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//獲取SQL信息
BoundSql boundSql = ms.getBoundSql(parameter);
//根據(jù)statementId、SQL語句、參數(shù)、分頁信息等生產(chǎn)緩存的Key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
//執(zhí)行重載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) {
//緩存命中,處理一級緩存中的輸出參數(shù)(存儲過程)
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//緩存未命中,從數(shù)據(jù)庫加載數(shù)據(jù),并將結(jié)果放入以及緩存
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) {
//如果當(dāng)前SQL的一級緩存配置為STATEMENT,查詢完既清空緩存
// issue #482
clearLocalCache();
}
}
return list;
}
可以看到在BaseExecutor::query中實現(xiàn)了一級緩存的基本邏輯。當(dāng)一級緩存沒有命中時會調(diào)用虛方法abstract doQuery,此時這個方法就等著子類去實現(xiàn)了。而在當(dāng)前的MyBatis版本中(3.5.5)BaseExecutor擁有如下子類:
-
BatchExecutor:執(zhí)行批處理操作時使用 -
SimpleExecutor:默認(rèn)配置,使用Statement執(zhí)行SQL語句,每一次執(zhí)行都會創(chuàng)建一個新的Statement。(至于使用PreparedStatement還是Statement執(zhí)行SQL語句,這取決于SQL標(biāo)簽中配置的statementType屬性,默認(rèn)使用PreparedStatement。下同) -
ReuseExecutor:使用Statement執(zhí)行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,如果沒有再創(chuàng)建 Statement stmt = prepareStatement(handler, ms.getStatementLog()); return handler.query(stmt, resultHandler); }
-
-
ClosedExecutor:關(guān)閉后的執(zhí)行器,內(nèi)部的所有方法都是未實現(xiàn)的-
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主要實現(xiàn)了一級緩存的功能,它只有在一級緩存未命中的情況下才會去真正執(zhí)行數(shù)據(jù)庫查詢。但是熟悉MyBatis的小伙伴應(yīng)該知道,MyBatis還提供了二級緩存的實現(xiàn),此時我們就需要將目光轉(zhuǎn)向CachingExecutor分支了。
CachingExcutor實際上是一個裝飾器,它封裝了二級緩存的實現(xiàn)邏輯
/**
* Executor的二級緩存裝飾器,它是實現(xiàn)MyBatis二級緩存的關(guān)鍵
* @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);
//從二級緩存中獲取數(shù)據(jù)
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
//二級緩存為空,才調(diào)用被裝飾的Executor的query獲取數(shù)據(jù),由于CachingExecutor大多數(shù)時候是用來裝飾SimpleExecutor對象,所以CachingExecutor
// 中的二級緩存邏輯會先執(zhí)行,如果二級緩存中沒有數(shù)據(jù),才會執(zhí)行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,都具備了二級緩存的功能。
走到這里,可能有很多小伙伴對二級緩存的實現(xiàn)原理仍然不太清楚,那這個故事還得從Executor的創(chuàng)建說起,我們先定位到Executor創(chuàng)建的地方DefaultSqlSessionFactory::openSession:
public SqlSession openSession() {
//從數(shù)據(jù)源中獲取連接,然后創(chuàng)建SqlSessionFactory
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
openSession方法直接調(diào)用了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);
//從數(shù)據(jù)源中獲取數(shù)據(jù)庫連接,然后創(chuàng)建Transaction對象
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//重點:根據(jù)配置創(chuàng)建Executor,該方法內(nèi)部會根據(jù)用戶是否配置二級緩存去決定是否創(chuàng)建二級緩存的裝飾器去裝飾Executor,這也是二級緩存是否生效的關(guān)鍵
final Executor executor = configuration.newExecutor(tx, execType);
//創(chuàng)建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方法中調(diào)用了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) {
//創(chuàng)建Batch類型的Executor
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
//創(chuàng)建Reuse類型的Executor
executor = new ReuseExecutor(this, transaction);
} else {
//創(chuàng)建simple類型的Executor
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
//如果配置了二級緩存,則用CachingExecutor裝飾前面創(chuàng)建的Executor,從而實現(xiàn)二級緩存
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
當(dāng)用戶配置二級緩存后,就會創(chuàng)建CachingExecutor裝飾器去包裝SimpleExecutor,這也是MyBatis二級緩存始終會優(yōu)先于一級緩存的原因。
3.3 項目經(jīng)理不干活
前面說了這么多,咱還是小小的總結(jié)一下吧。SqlSession的所有查詢方法最后都會交給selectList來執(zhí)行,而selectList將任務(wù)下發(fā)給了項目經(jīng)理(Executor)去執(zhí)行,項目經(jīng)理會先去查二級緩存有沒有已經(jīng)緩存的結(jié)果,如果沒有則會去查一級緩存,如果還是沒有命中則會去執(zhí)行doQuery方法操作數(shù)據(jù)庫。
好的,那我們把視角轉(zhuǎn)回到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();
//根據(jù)SQL標(biāo)簽中配置statementType來創(chuàng)建不同的StatementHandler實現(xiàn)
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//創(chuàng)建Statement,并使用parameterHandler對占位符進行賦值
stmt = prepareStatement(handler, ms.getStatementLog());
//通過statementHandler對象調(diào)用ResultSetHandler將結(jié)果集轉(zhuǎn)化為指定對象返回
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
doQuery方法實際上也沒有真正執(zhí)行數(shù)據(jù)庫操作,而是將這個艱巨任務(wù)交給了StatementHandler去執(zhí)行了,而這樣看上去確實符合項目經(jīng)理的調(diào)性,自己負責(zé)分配任務(wù),苦逼程序員負責(zé)完成需求,哈哈。
跳回到計算機的世界,這種設(shè)計思想也完美符合設(shè)計模式中的單一職責(zé)原則,Executor只負責(zé)一二級緩存的實現(xiàn),而數(shù)據(jù)庫的操作交由*Handler實現(xiàn)。
四. 苦逼程序員
領(lǐng)導(dǎo)將任務(wù)下發(fā)后,就需要底下的項目組成員去完成了。而在MyBatis中一次操作任務(wù)會下發(fā)給三個“程序員”,它們分別負責(zé)不同的任務(wù):
- ParameterHandler:對預(yù)編譯的SQL語句進行參數(shù)設(shè)置,
DefualtParameterHandler是其實現(xiàn)類 - StatementHandler: 執(zhí)行SQL語句,獲取JDBC返回的
ResultSet, - ResultSetHandler:對數(shù)據(jù)庫返回的結(jié)果集進行封裝,
DefaultResultSetHandler是其實現(xiàn)類
4.1 ParameterHandler
在3.3節(jié)中,我們分析了SimpleExecutor:doQuery方法,其內(nèi)部調(diào)用了SimpleExecutor::prepareStatement方法用于獲取PreparedStatement實例并設(shè)置占位符的參數(shù)值:
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//試圖創(chuàng)建帶有日志功能的Connection,也就是ConnectionLogger
Connection connection = getConnection(statementLog);
//試圖創(chuàng)建帶有日志功能PreparedStatement,也就是PreparedStatementLogger
stmt = handler.prepare(connection, transaction.getTimeout());
//設(shè)置參數(shù)值
handler.parameterize(stmt);
return stmt;
}
可以看到SimpleExecutor::prepareStatement方法調(diào)用了Statement:parameterize方法,我們進入PreparedStatementHandler一探究竟:
public void parameterize(Statement statement) throws SQLException {
//設(shè)置參數(shù)值
parameterHandler.setParameters((PreparedStatement) statement);
}
可以看到,設(shè)置參數(shù)值的操作最終是交給了ParameterHandler去實現(xiàn):
/**
* 對預(yù)編譯的SQL語句進行參數(shù)設(shè)置
* @author Clinton Begin
* @author Eduardo Macarron
*/
public class DefaultParameterHandler implements ParameterHandler {
//TypeHandler注冊中心
private final TypeHandlerRegistry typeHandlerRegistry;
//對應(yīng)的SQL節(jié)點信息
private final MappedStatement mappedStatement;
//用戶傳入的參數(shù)
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語句的占位符對應(yīng)的參數(shù)信息
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) {//當(dāng)參數(shù)為存儲過程輸出參數(shù)則不處理
//綁定的實參
Object value;
//參數(shù)的名稱
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
//如果SQL中的參數(shù)列表中包含這個參數(shù),則獲取值
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();
//獲取參數(shù)對應(yīng)的jdbcType
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
//由typeHandler設(shè)置參數(shù)值
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進行數(shù)據(jù)庫操作的核心是由StatementHandler完成的,而StatementHandler只是一個接口,它擁有如下幾個實現(xiàn)類:
- BaseStatementHandler:所有子類的父類,模板方法模式,內(nèi)部定義了數(shù)據(jù)庫操作的操作步驟,核心功能交由子類實現(xiàn)。
- SimpleStatmentHandler :使用Statement對象訪問數(shù)據(jù)庫,無須參數(shù)化;
- PreparedStatmentHandler:使用預(yù)編譯PrepareStatement對象訪問數(shù)據(jù)庫;
- CallableStatmentHandler :調(diào)用存儲過程;

我們再次回到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();
//根據(jù)SQL標(biāo)簽中配置statementType來創(chuàng)建不同的StatementHandler實現(xiàn)
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//創(chuàng)建Statement,并使用parameterHandler對占位符進行賦值
stmt = prepareStatement(handler, ms.getStatementLog());
//通過statementHandler對象調(diào)用ResultSetHandler將結(jié)果集轉(zhuǎn)化為指定對象返回
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
doQuery最后調(diào)用的是StamentHandler::query方法,我們進入其實現(xiàn)類中(PreparedStatementHandler::query)可以看到最終還是調(diào)用的JDBC中PreparedStatement::execute方法:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//執(zhí)行SQL語句
ps.execute();
//封裝結(jié)果集并返回對象
return resultSetHandler.handleResultSets(ps);
}
4.3 ResultSetHandler
ResultSetHandler的唯一實現(xiàn)類DefaultResultSetHandler用于對返回結(jié)果集進行包裝。
DefaultResultSetHandler::handleResultSets源碼:
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
//用于保存結(jié)果集對象
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
//statement可能返回多個結(jié)果集對象,這里先取出第一個結(jié)果集
ResultSetWrapper rsw = getFirstResultSet(stmt);
//獲取映射規(guī)則,對應(yīng)映射配置文件中resultMap標(biāo)簽。有時候sql標(biāo)簽的resultMap屬性會指定多個結(jié)果集。
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
//結(jié)果集和resultMap不能為空,為空拋出異常
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
//循環(huán)處理多個結(jié)果集
//獲取當(dāng)前結(jié)果集對應(yīng)的resultMap
ResultMap resultMap = resultMaps.get(resultSetCount);
//根據(jù)映射規(guī)則對結(jié)果進行轉(zhuǎn)化,轉(zhuǎn)換成目標(biāo)對象以后放入multipleResults
handleResultSet(rsw, resultMap, multipleResults, null);
//獲取下一個結(jié)果集
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);
}
封裝單個結(jié)果集:
/**
* 封裝單個結(jié)果集數(shù)據(jù),將結(jié)果保存至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為空則,實例化一個默認(rèn)的resultHandler
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
//封裝結(jié)果集數(shù)據(jù),然后將數(shù)據(jù)暫存在defaultResultHandler中
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
//將暫存在defaultResultHandler中的數(shù)據(jù)放入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()) {
//處理嵌套結(jié)果集的情況
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
//處理沒有嵌套結(jié)果集的情況
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
/**
* 根據(jù)一個簡單ResultMap規(guī)則,封裝結(jié)果集數(shù)據(jù),然后將數(shù)據(jù)暫存至resultHandler
* @param rsw
* @param resultMap
* @param resultHandler 封裝結(jié)果的暫存區(qū)
* @param rowBounds
* @param parentMapping
* @throws SQLException
*/
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
//創(chuàng)建結(jié)果上下文,所謂的上下文就是專門在循環(huán)中緩存結(jié)果對象的
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
ResultSet resultSet = rsw.getResultSet();
//根據(jù)分頁信息,定位到指定記錄
skipRows(resultSet, rowBounds);
//循環(huán)處理每一行數(shù)據(jù)
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
//進一步完善resultMap信息,主要處理鑒別器信息
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
//重點解讀:讀取ResultSet中的一行數(shù)據(jù)進行映射,轉(zhuǎn)化并返回目標(biāo)對象
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
//保存映射結(jié)果對象
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
封裝單行數(shù)據(jù):
/**
* 讀取一行數(shù)據(jù),并封裝成指定類型對象中,實例封裝的核心
* @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();
//創(chuàng)建封裝對象,空的
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
//結(jié)果集不為空,且存在結(jié)果對象的類型處理程序
//封裝MetaObject對象,方便賦值
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
if (shouldApplyAutomaticMappings(resultMap, false)) {
//進行自動映射。一般情況下autoMappingBehavior的默認(rèn)值為PARTIAL,對未明確指定映射規(guī)則的字段進行自動映射
//是否自動映射由resultMap標(biāo)簽中的autoMapping屬性決定,默認(rèn)值為true
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
//進行屬性映射
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
//如果發(fā)現(xiàn)數(shù)據(jù)或者懶加載機制中還有沒有加載出來的數(shù)據(jù)
foundValues = lazyLoader.size() > 0 || foundValues;
//如果沒有數(shù)據(jù)且不允許空行返回實例,則將rowValue置為空
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
五. 時序圖

由于圖片版幅較大,網(wǎng)頁顯示字體會看不清,這里給出下載鏈接:MyBatis核心調(diào)用流程時序圖-GitHub;MyBatis核心調(diào)用流程時序圖-Gitee
六. 總結(jié)
SqlSession作為MyBatis框架的核心接口隱藏了底層Executor接口以及xxxHandler接口復(fù)雜的實現(xiàn)邏輯。用戶對SqlSession的操作實際上都交由Executor去執(zhí)行,在Executor處理完一二級緩存邏輯后,就將數(shù)據(jù)庫操作以及結(jié)果集封裝的工作全部交由ParameterHandler、StatementHandler、ResultSetHandler去完成。
本文只是展現(xiàn)了SqlSession查詢數(shù)據(jù)庫框架的基本實現(xiàn)流程,更加細節(jié)的信息需要小伙伴們自己去挖掘。博主自己對MyBatis源碼進行了詳細注釋,如有需要,請移步至:GitHub或Gitee
本文闡述了自己對MyBatis源碼的一些理解,如有不足,歡迎大佬指點,感謝感謝!!

浙公網(wǎng)安備 33010602011771號