上篇我們分析了mybatis在啟動(dòng)過(guò)程中是如何加載配置文件的,本篇我們繼續(xù)來(lái)分析一下mybatis在創(chuàng)建完Configuration配置后,是如何進(jìn)行查詢的(本篇重點(diǎn)是查詢實(shí)現(xiàn),mybatis的緩存方面不會(huì)詳細(xì)分析,后續(xù)文章會(huì)涉及mybatis的緩存)。
1、通過(guò)SqlSessionFactory創(chuàng)建SqlSession
我們上篇分析完了mybatis解析配置文件創(chuàng)建Configuration,根據(jù)Configuration創(chuàng)建SqlSessionFactory的過(guò)程?,F(xiàn)在,我們看一下mybatis是如何通過(guò)SqlSessionFactory創(chuàng)建SqlSession的。我們先看看SqlSessionFactory接口有哪些方法:
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
SqlSessionFactory顧名思義,是用來(lái)創(chuàng)建SqlSession的,我們看一下默認(rèn)的DefaultSqlSessionFactory的實(shí)現(xiàn),這里只貼出主要的一個(gè)方法:
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
try {
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException e)
autoCommit = true;
}
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
final Transaction tx = transactionFactory.newTransaction(connection);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
我們可以看到,創(chuàng)建Sql需要Executor,而創(chuàng)建Executor需要Transaction對(duì)象,我們先來(lái)看一下Transaction接口:
public interface Transaction {
Connection getConnection() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
Integer getTimeout() throws SQLException;
}
Transaction接口給我們提供了事物基本操作的封裝。
而Executor也在Transaction的基礎(chǔ)上進(jìn)行了封裝:
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement ms, Object parameter) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
boolean isCached(MappedStatement ms, CacheKey key);
void clearLocalCache();
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
Transaction getTransaction();
void close(boolean forceRollback);
boolean isClosed();
void setExecutorWrapper(Executor executor);
}
實(shí)現(xiàn)這兩個(gè)接口的之類有很多,所以我們暫時(shí)先不看這兩個(gè)接口的具體實(shí)現(xiàn)。在創(chuàng)建完這兩個(gè)接口的實(shí)現(xiàn)對(duì)象后,SqlSessionFactory會(huì)創(chuàng)建SqlSession。我們以DefaultSqlSession為例子,來(lái)看一下我們常用的幾個(gè)操作接口是如何實(shí)現(xiàn)的。
1、使用SqlSession操作數(shù)據(jù)庫(kù)的實(shí)現(xiàn)原理
1.1、使用selectOne()方法進(jìn)行查詢
我們先來(lái)看一下方法的實(shí)現(xiàn):
@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.<T>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()方法是通過(guò)selectList()來(lái)實(shí)現(xiàn)的,當(dāng)查詢出的結(jié)果多余1個(gè)時(shí)就會(huì)跑出異常。那接下來(lái)我們看看selectList()的實(shí)現(xiàn)。
1.2、使用selectList()方法進(jìn)行查詢
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
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();
}
}
我們可以看到selectList的執(zhí)行過(guò)程:首先根據(jù)id從configuration中查詢出對(duì)應(yīng)的MappedStatement對(duì)象(上篇已經(jīng)介紹了MappedStatement,這里就不多說(shuō)了),獲取到MappedStatement對(duì)象后,將MappedStatement對(duì)象和傳入的參數(shù)交給executor來(lái)執(zhí)行具體的操作,我們以BaseExecutor為例,看一下具體的實(shí)現(xiàn):
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
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.");
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();
}
deferredLoads.clear(); // issue #601
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache(); // issue #482
}
}
return list;
}
我們略過(guò)有關(guān)mybatis緩存的操作,只看具體查詢執(zhí)行的過(guò)程,我們可以看到executor查詢真正執(zhí)行是通過(guò)調(diào)用queryFromDatabase()方法來(lái)實(shí)現(xiàn)的,我們一步一步跟蹤方法調(diào)用鏈會(huì)發(fā)現(xiàn),queryFromDatabase()方法最后會(huì)調(diào)用子類的
doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) 方法實(shí)現(xiàn)的。Executor有三個(gè)子類:BatchExecutor、ReuseExecutor和SimpleExecutor,我們分別看一下三個(gè)子類對(duì)doQuery方法的實(shí)現(xiàn):
首先我們看一下SimpleExecutor的實(shí)現(xiàn):
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();
//將查詢數(shù)據(jù)封裝成StatementHandler進(jìn)行查詢
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
SimpleExecutor將數(shù)據(jù)封裝成StatementHandler來(lái)進(jìn)行查詢,我們先來(lái)看一下封裝的過(guò)程:
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//調(diào)用mybatis生命周期中的方法,mybatis插件應(yīng)用
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
//RoutingStatementHandler的構(gòu)造方法
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
我們可以看到這里直接創(chuàng)建了一個(gè)RoutingStatementHandler的對(duì)象,其實(shí)RoutingStatementHandler是一個(gè)代理對(duì)象,RoutingStatementHandler根據(jù)定義的StatementType來(lái)創(chuàng)建真正的StatementHandler。熟悉jdbc的同學(xué)應(yīng)該對(duì)StatementType都不會(huì)陌生,StatementType有三種:1.Statement、2.PrepareStatement、3.CallableStatement。具體的區(qū)別這里不多做解釋,不清楚的同學(xué)可以自己百度。三種不同的StatementType對(duì)應(yīng)著SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler。
我們繼續(xù)回到doQuery方法,在創(chuàng)建完StatementHandler后,SimpleExecutor會(huì)調(diào)用prepareStatement()方法來(lái)獲取Statement對(duì)象,然后將Statement對(duì)象交給StatementHandler來(lái)執(zhí)行查詢,最后返回最終的結(jié)果。我們可以看到prepareStatement()方法分別調(diào)用了StatementHandler的prepare()方法和parameterize()方法來(lái)準(zhǔn)備Statement。我們先來(lái)看一下prepare()方法的實(shí)現(xiàn),這個(gè)方法在BaseStatementHandler這個(gè)抽象類中:
public Statement prepare(Connection connection) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
// instantiateStatement方法是一個(gè)抽象方法,交給子類實(shí)現(xiàn)
statement = instantiateStatement(connection);
setStatementTimeout(statement);
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);
}
}
創(chuàng)建Statement的過(guò)程是通過(guò)instantiateStatement()方法交給子類實(shí)現(xiàn)的,我們貼出來(lái)三個(gè)子類的實(shí)現(xiàn)細(xì)節(jié):
// SimpleStatementHandler
protected Statement instantiateStatement(Connection connection) throws SQLException {
if (mappedStatement.getResultSetType() != null) {
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.createStatement();
}
}
// PreparedStatementHandler
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.prepareStatement(sql);
}
}
// CallableStatementHandler
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getResultSetType() != null) {
return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.prepareCall(sql);
}
}
PrepareStatemnet和CallableStatement都會(huì)先通過(guò)BoundSql對(duì)象來(lái)獲取真正執(zhí)行的sql語(yǔ)句,這一塊解析我們后面再講。然后會(huì)通過(guò)jdbc的Connection來(lái)獲取Statement。
看完Statement的創(chuàng)建過(guò)程,我們繼續(xù)回到prepareStatement()方法,我們都知道,PrepareStatement方法是執(zhí)行預(yù)編譯查詢的Statement,在執(zhí)行查詢之前需要設(shè)置對(duì)應(yīng)的參數(shù),而CallableStatement用來(lái)執(zhí)行存儲(chǔ)過(guò)程,也需要設(shè)置參數(shù)信息,這些操作都是放在StatementHandler的parameterize()方法里處理,我們以PrepareStatement為例子,看一下參數(shù)是如何設(shè)置的,通過(guò)方法追蹤,我們能夠找到設(shè)置參數(shù)是ParameterHandler的setParameters()方法實(shí)現(xiàn)的:
public void setParameters(PreparedStatement ps) throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject);
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
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
value = metaObject == null ? null : metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
if (typeHandler == null) {
throw new ExecutorException("There was no TypeHandler found for parameter " + propertyName + " of statement " + mappedStatement.getId());
}
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}
這里根據(jù)解析mapper文件的parameterType結(jié)果來(lái)進(jìn)行參數(shù)的設(shè)置,具體邏輯并不復(fù)雜,有興趣的同學(xué)可以自己看一下。
這里創(chuàng)建Statement的邏輯我們就看完了,繼續(xù)回到之前的public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) 方法,創(chuàng)建完Statement后,具體查詢還是會(huì)交給StatementHandler的query()方法來(lái)執(zhí)行,StatementHandler,我們以PrepareStatementHandler為例看一下具體的實(shí)現(xiàn):
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
執(zhí)行完查詢之后,會(huì)把執(zhí)行結(jié)果交給ResultSetHandler來(lái)解析,我們看一下具體實(shí)現(xiàn)方法:
public List<Object> handleResultSets(Statement stmt) throws SQLException {
final List<Object> multipleResults = new ArrayList<Object>();
final List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
int resultSetCount = 0;
ResultSet rs = stmt.getResultSet();
while (rs == null) {
// move forward to get the first resultset in case the driver
// doesn't return the resultset as the first result (HSQLDB 2.1)
if (stmt.getMoreResults()) {
rs = stmt.getResultSet();
} else {
if (stmt.getUpdateCount() == -1) {
// no more results. Must be no resultset
break;
}
}
}
validateResultMapsCount(rs, resultMapCount);
while (rs != null && resultMapCount > resultSetCount) {
final ResultMap resultMap = resultMaps.get(resultSetCount);
ResultColumnCache resultColumnCache = new ResultColumnCache(rs.getMetaData(), configuration);
//處理查詢結(jié)果結(jié)果
handleResultSet(rs, resultMap, multipleResults, resultColumnCache);
rs = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
return collapseSingleResultList(multipleResults);
}
邏輯很簡(jiǎn)單,就是交給方法handleResultSet()方法來(lái)處理,我們一步一步跟蹤,發(fā)現(xiàn)處理會(huì)交給handleRowValues()方法進(jìn)行處理,handleRowValues()處理的邏輯是首先跳過(guò)指定行數(shù)(RowBounds設(shè)置的,默認(rèn)不跳過(guò)),然后遍歷ResultSet,這里要注意,遍歷ResultSet的時(shí)候,遍歷有個(gè)上限,也是RowBounds指定,默認(rèn)為Integer的最大值。在遍歷的過(guò)程中將結(jié)果集進(jìn)行解析,我們看一下實(shí)現(xiàn)細(xì)節(jié):
protected void handleRowValues(ResultSet rs, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultColumnCache resultColumnCache) throws SQLException {
final DefaultResultContext resultContext = new DefaultResultContext();
//跳過(guò)指定行
skipRows(rs, rowBounds);
//限制遍歷結(jié)果集個(gè)數(shù)
while (shouldProcessMoreRows(rs, resultContext, rowBounds)) {
//選擇真正的ResultMap(mybatis的discriminator功能)
final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rs, resultMap, null);
//創(chuàng)建查詢結(jié)果對(duì)象
Object rowValue = getRowValue(rs, discriminatedResultMap, null, resultColumnCache);
//注冊(cè)到結(jié)果集中
callResultHandler(resultHandler, resultContext, rowValue);
}
}
我們只看遍歷的過(guò)程,首先通過(guò)resolveDiscriminatedResultMap()方法獲取到真正要使用的ResultMap,這個(gè)過(guò)程很簡(jiǎn)單,就是獲取discriminator 標(biāo)簽定義的colum的值,然后獲取該值對(duì)應(yīng)的ResultMap,但是要注意,這個(gè)是一個(gè)循環(huán)的過(guò)程(解析discriminator里映射的ResultMap還有discriminator標(biāo)簽)。這里源碼不貼出,有興趣的同學(xué)可以看一下。
獲取到對(duì)應(yīng)的ResultMap后,通過(guò)getRowValue()來(lái)進(jìn)行創(chuàng)建查詢結(jié)果對(duì)象,創(chuàng)建的過(guò)程可以概括為下面幾個(gè)步驟:
1、判斷要返回的結(jié)果的類型是否有TypeHandler,有的話使用TypeHandler來(lái)進(jìn)行創(chuàng)建結(jié)果對(duì)象;
2、判斷resultMap中是否指定構(gòu)造方法,有的話使用構(gòu)造方法創(chuàng)建結(jié)果對(duì)象;
3、如果沒(méi)有TypeHandler和指定構(gòu)造方法,使用objectFactory來(lái)進(jìn)行創(chuàng)建對(duì)象;
4、創(chuàng)建完對(duì)象之后,如果不是通過(guò)TypeHandler進(jìn)行創(chuàng)建,則需要設(shè)置對(duì)象的屬性值(是否進(jìn)行參數(shù)設(shè)置還需要根據(jù)automapping屬性來(lái)進(jìn)行判斷)
5、如果需要設(shè)置屬性,通過(guò)反射進(jìn)行設(shè)置
6、通過(guò)反射設(shè)置resultMap中property屬性指定的映射的值
protected Object getRowValue(ResultSet rs, ResultMap resultMap, CacheKey rowKey, ResultColumnCache resultColumnCache) throws SQLException {
final ResultLoaderMap lazyLoader = instantiateResultLoaderMap();
//創(chuàng)建對(duì)象
Object resultObject = createResultObject(rs, resultMap, lazyLoader, null, resultColumnCache);
// 如果不是通過(guò)TypeHandler進(jìn)行創(chuàng)建,則需要設(shè)置對(duì)象的屬性值
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(resultObject);
boolean foundValues = resultMap.getConstructorResultMappings().size() > 0;
if (shouldApplyAutomaticMappings(resultMap, !AutoMappingBehavior.NONE.equals(configuration.getAutoMappingBehavior()))) {
final List<String> unmappedColumnNames = resultColumnCache.getUnmappedColumnNames(resultMap, null);
//設(shè)置需要自動(dòng)配置的屬性值
foundValues = applyAutomaticMappings(rs, unmappedColumnNames, metaObject, null, resultColumnCache) || foundValues;
}
final List<String> mappedColumnNames = resultColumnCache.getMappedColumnNames(resultMap, null);
// 通過(guò)反射設(shè)置resultMap中property屬性指定的映射的值
foundValues = applyPropertyMappings(rs, resultMap, mappedColumnNames, metaObject, lazyLoader, null) || foundValues;
foundValues = (lazyLoader != null && lazyLoader.size() > 0) || foundValues;
resultObject = foundValues ? resultObject : null;
return resultObject;
}
return resultObject;
}
將查詢結(jié)果解析成對(duì)象之后,繼續(xù)調(diào)用callResultHandler()來(lái)對(duì)結(jié)果集進(jìn)一步處理,解析成map或list。這里我們就不多解釋了。
到這里SimpleExecutor就介紹完了,我們繼續(xù)看一下ReuseExecutor:ReuseExecutor顧名思義,是可以重用的Executor。ReuseExecutor會(huì)緩存Statement,以sql為keyStatement為value緩存,這里不多介紹了。
最后我們看一下BatchExecutor:BatchExecutor繼承了BaseExecutor,來(lái)講多次查詢或者更新合并成一個(gè)。熟悉jdbc的同學(xué)應(yīng)該對(duì)這個(gè)不陌生。這里不多講,有興趣的同學(xué)也可以看一下,邏輯并不復(fù)雜。到這里SqlSession的selectList()的方法的邏輯已經(jīng)分析完了。接下來(lái)我們看看SqlSession的selectMap()方法。
1.3、使用selectMap()方法進(jìn)行查詢
我們先看一下DefaultSqlSession的selectMap()方法:
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
final List<?> list = selectList(statement, parameter, rowBounds);
final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<K, V>(mapKey,
configuration.getObjectFactory(), configuration.getObjectWrapperFactory());
final DefaultResultContext context = new DefaultResultContext();
for (Object o : list) {
context.nextResultObject(o);
mapResultHandler.handleResult(context);
}
Map<K, V> selectedMap = mapResultHandler.getMappedResults();
return selectedMap;
}
我們發(fā)現(xiàn),selectMap()操作也是先進(jìn)行selectList()查詢出結(jié)果集,再使用DefaultMapResultHandler來(lái)做一步轉(zhuǎn)換。邏輯也很簡(jiǎn)單。有興趣的同學(xué)可以自己閱讀一下源碼。
1.4、使用Mapper接口進(jìn)行查詢
我們?cè)偈褂胢ybatis的過(guò)程中,使用最多的操作應(yīng)該是使用Mapper接口進(jìn)行查詢:
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
我們看一下Mapper具體的獲取過(guò)程:
//sqlSession的getMapper方法
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
//configuration的getMapper方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
// mapperRegistry的getMapper方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null)
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
獲取Mapper的過(guò)程就是從configuration中的MapperRegistry中獲取已經(jīng)注冊(cè)的mapperProxyFactory,然后通過(guò)mapperProxyFactory獲取Mapper,注冊(cè)是發(fā)生在mybatis解析configuration的過(guò)程中。我們可以先來(lái)看一下mapper的注冊(cè)過(guò)程:
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<T>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
注冊(cè)過(guò)程很簡(jiǎn)單,就是new一個(gè)MapperProxyFactory對(duì)象,我們看看MapperProxyFactory的實(shí)現(xiàn):
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
熟悉java反射的同學(xué)應(yīng)該很容易看懂MapperProxyFactory。其中MapperProxy實(shí)現(xiàn)了InvocationHandler接口,作為代理的處理邏輯。我們可以看一下具體的實(shí)現(xiàn)過(guò)程:
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
//獲取MapperMethod對(duì)象,使用MapperMethod來(lái)執(zhí)行具體的操作
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
//獲取和構(gòu)造MapperMethod對(duì)象
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
我們可以看一下MapperMethod的execute()方法:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
if (SqlCommandType.INSERT == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
} else if (SqlCommandType.SELECT == command.getType()) {
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
} else {
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
我們可以看到,在使用Mapper接口進(jìn)行操作時(shí),代理會(huì)根據(jù)方法的簽名信息來(lái)選擇執(zhí)行操作的類型,然后使用SqlSession來(lái)執(zhí)行具體的操作。
好了,到此mybatis的查詢分析就介紹到這里。為了總結(jié)上面的操作,我畫(huà)了一個(gè)簡(jiǎn)單的時(shí)序圖幫助理解:
