Mybatis架構相關的知識
如上所示,這是一個簡單的Mybaits執行流程.
我們其實可以看到,一直到第三步(Sqlsession)那麼一步,這都是我們的程式裡需要建立的.而之後的步驟才是底層完成的任務.
這裡就有了一個引申的概念,四大物件。
1 executor 呼叫
StatementHandler
,以此來執行SQL語句。這個是結構圖.
BaseExecutor
:這個執行器執行了最基本的功能,其實如果你用debug就能發現。其餘幾個執行器執行時,也會呼叫這個BaseExecutor
中的方法。SimpleExecutor
:最簡單的執行器,根據對應的sql直接執行即可,不會做一些額外的操作;拼接完SQL之後,直接交給 StatementHandler 去執行。BatchExecutor
:通過批量操作來優化效能。通常需要注意的是批量更新操作,由於內部有快取的實現,使用完成後記得呼叫flushStatements
來清除快取。ReuseExecutor
:可重用的執行器,重用的物件是Statement,也就是說該執行器會快取同一個sql的Statement,省去Statement的重新建立,優化效能。內部的實現是通過一個HashMap來維護Statement物件的。由於當前Map只在該session中有效,所以使用完成後記得呼叫
flushStatements
來清除Map。CachingExecutor
:啟用於二級快取時的執行器;採用靜態代理;代理一個 Executor 物件。
執行 update 方法前判斷是否清空二級快取;
執行 query 方法前先在二級快取中查詢,命中失敗再通過被代理類查詢。
它使用StaementHandler實現JDBC的操作的。
2 parameterHandler:
這個主要是對引數進行操作。
public void setParameters(PreparedStatement ps) throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
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;
//`獲取javaBean中的屬性名稱(如果你用javaBean作為引數輸入到SQL語句中,SQL不也是會為你分配到相應的sql中嘛。
//這個就是)
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 {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
//獲得typeHandler。這是一種可以用來對輸入的值進行處理的函式,將java資料型別轉化為資料庫資料型別。
//你既可以自己定義,Mybatis也為你提供了一些基本的。
TypeHandler typeHandler = parameterMapping.getTypeHandler();
//jdbcType是對應的sql引數(這個是你在XML檔案中設定的,用來對應javaType和jdbcType)JDBCtYPE是一個列舉型別
// <result property="FLD_NUMBER" column="FLD_NUMBER" javaType="double" jdbcType="NUMERIC"/>
JdbcType jdbcType = parameterMapping.getJdbcType();
//如果兩者都為空
if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
//
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}
//設定引數
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
如果引數列表為空
if (parameter == null) {
對應的jdbcType為空
if (jdbcType == null) {
可以為空的引數必須設定jdbcType,否則就報錯。
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
"Cause: " + e, e);
}
} else {
//根據傳入引數的不同,呼叫不同種類的set函式,讓其進行替換。
setNonNullParameter(ps, i, parameter, jdbcType);
}
}
3 ResultSetHandler
對結果進行操作,將SQL的查詢加入到java型別之中。
public interface ResultSetHandler {
// 將Statement執行後產生的結果集(可能有多個結果集)對映為結果列表
<E> List<E> handleResultSets(Statement stmt) throws SQLException; <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
// 處理儲存過程執行後的輸出引數
void handleOutputParameters(CallableStatement cs) throws SQLException; }
3. DefaultResultSetHandler
ResultSetHandler的具體實現類是DefaultResultSetHandler,其實現的步驟就是將Statement執行後的結果集,按照Mapper檔案中配置的ResultType或ResultMap來封裝成對應的物件,最後將封裝的物件返回 。
以最常用的 handleResultSets 為例進行簡單的分析:
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); final List<Object> multipleResults = new ArrayList<Object>(); int resultSetCount = 0;
// 第一個結果集
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 獲取 resultMap
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
// 判斷 ResultMap 是否為空,空則拋異常
validateResultMapsCount(rsw, resultMapCount);
// 處理第一個結果集
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 將結果集對映為對應的 ResultMap 物件
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
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);
}
4 StatementHandler:
這個引數操作了SQL語句的整個執行流程。
這個我以前有文章進行過講解,所以這個就先算了。就不講了。
既然對於四大介面(四大物件)有了基本的瞭解。那麼我們就看看下面的圖片。我想應該不算很難吧。
引數對映部分算是對應:parameterHandler:
XML配置載入部分可以看看如下這張圖片:
引數對映部分在這篇文章中
之後是SQL語句執行:
這個步驟在我看來是對應StatementHandler
結果對映則是由ResultSetHandler
完成的.
(當然,這個就是理解一下四大物件在Mybatis中發生什麼作用)
各種常用的Mybatis主要作用。
- BoundSql類,封裝mybatis最終產生sql的類,包括sql語句,引數,引數源資料等引數:
- Configuration類:就像是MyBatis的總管,裡麵包含了所有的資訊。無論是連線