mybatis原始碼解讀:executor包(結果處理功能)
技術標籤:mybaits原始碼mybatis
歡迎關注本人公眾號:
mybatis查詢結果的處理需要完成的步驟有:
1.處理結果對映中的巢狀對映等邏輯
2.根據對映關係,生成結果物件
3.根據資料庫查詢記錄對結果物件的屬性進行賦值
4.將結果物件彙總為List,Map,Cursor等形式。
1.結果處理功能
其中executor包中的result子包將負責完成"將結果物件彙總為List,Map,Cursor等形式"的簡單功能的一部分。
result子包中主要有3個類:DefaultResultContext類、DefaultResultHandler類、DefaultMapResultHandler類。
DefaultResultContext類用來儲存一個結果物件,對應資料庫的一條記錄。
public class DefaultResultContext<T> implements ResultContext<T> {
// 結果物件
private T resultObject;
// 結果計數(表明這是第幾個結果物件)
private int resultCount;
// 使用完畢(結果已經被取走)
privatebooleanstopped;
}
DefaultResultHandler類負責將DefaultResultContext類中的結果物件聚合成一個List返回。
public class DefaultResultHandler implements ResultHandler<Object> {
private final List<Object> list;
public DefaultResultHandler() {
list = new ArrayList<>();
}
@SuppressWarnings("unchecked")
public DefaultResultHandler(ObjectFactory objectFactory) {
list = objectFactory.create(List.class);
}
@Override
public void handleResult(ResultContext<?> context) {
list.add(context.getResultObject());
}
public List<Object> getResultList() {
return list;
}
}
DefualtMapResultHandler類負責將DefaultResultContext類中的結果物件聚合成一個Map返回。
public class DefaultMapResultHandler<K, V> implements ResultHandler<V> {
// Map形式的對映結果
private final Map<K, V> mappedResults;
// Map的鍵。由使用者指定,是結果物件中的某個屬性名
private final String mapKey;
// 物件工廠
private final ObjectFactory objectFactory;
// 物件包裝工廠
private final ObjectWrapperFactory objectWrapperFactory;
// 反射工廠
private final ReflectorFactory reflectorFactory;
/**
* 處理一個結果
* @param context 一個結果
*/
@Override
public void handleResult(ResultContext<? extends V> context) {
// 從結果上下文中取出結果物件
final V value = context.getResultObject();
// 獲得結果物件的元物件
final MetaObject mo = MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
// 基於元物件取出key對應的值
final K key = (K) mo.getValue(mapKey);
mappedResults.put(key, value);
}
public Map<K, V> getMappedResults() {
return mappedResults;
}
}
2.結果集處理功能
其中executor包中的resultset子包負責的功能有:
1.處理結果對映中的巢狀對映等邏輯
2.根據對映關係,生成結果物件
3.根據資料庫查詢記錄對結果物件的屬性進行賦值
resultset子包主要有ResultSetWrapper結果封裝類,ResultSetHandler和DefaultResultSetHandler分別是結果集處理器的介面和實現類。
ResultSetWrapper類是對java.sql.ResultSet介面的進一步封裝,在此基礎上擴展出更多的功能,用到了裝飾器模式。
public class ResultSetWrapper {
// 被裝飾的resultSet物件
private final ResultSet resultSet;
// 型別處理器登錄檔
private final TypeHandlerRegistry typeHandlerRegistry;
// resultSet中各個列對應的列名列表
private final List<String> columnNames = new ArrayList<>();
// resultSet中各個列對應的Java型別名列表
private final List<String> classNames = new ArrayList<>();
// resultSet中各個列對應的JDBC型別列表
private final List<JdbcType> jdbcTypes = new ArrayList<>();
// <列名,< java型別,TypeHandler>>
// 這裡的資料是不斷組建起來的。java型別傳入,然後去全域性handlerMap索引java型別的handler放入map,然後在賦給列名。
// 每個列後面的java型別不應該是唯一的麼?不是的
// <resultMap id="userMapFull" type="com.example.demo.UserBean">
// <result property="id" column="id"/>
// <result property="schoolName" column="id"/>
// </resultMap>
// 上面就可能不唯一,同一個列可以給不同的java屬性
// 型別與型別處理器的對映表。結構為:Map<列名,Map<Java型別,型別處理器>>
private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<>();
// 記錄了所有的有對映關係的列。
// key為resultMap的id,後面的List為該resultMap中有對映的列的列表
// <resultMap的id,List<物件對映的列名>>
// 記錄了所有的有對映關係的列。結構為:Map<resultMap的id,List<物件對映的列名>>
private final Map<String, List<String>> mappedColumnNamesMap = new HashMap<>();
// 記錄了所有的無對映關係的列。
// key為resultMap的id,後面的List為該resultMap中無對映的列的列表
// // <resultMap的id : List<物件對映的列名>>
// 記錄了所有的無對映關係的列。結構為:Map<resultMap的id,List<物件對映的列名>>
privatefinalMap<String,List<String>>unMappedColumnNamesMap=newHashMap<>();
/**
* 如果通過列名、屬性型別找到之前存好的handler,則就是它
* 如果通過列名沒找到:
* 1、通過列名找到其jdbc型別
* 2、根據java型別、jdbc型別,找到對應的handler
*
* @param propertyType
* @param columnName
* @return
*/
public TypeHandler<?> getTypeHandler(Class<?> propertyType, String columnName) {
TypeHandler<?> handler = null;
Map<Class<?>, TypeHandler<?>> columnHandlers = typeHandlerMap.get(columnName);
if (columnHandlers == null) {
columnHandlers = new HashMap<>();
typeHandlerMap.put(columnName, columnHandlers);
} else {
handler = columnHandlers.get(propertyType);
}
// 如果之前沒有,則找到後放入備用
if (handler == null) {
JdbcType jdbcType = getJdbcType(columnName);
// 根據型別去全域性尋找對應的handler
handler=typeHandlerRegistry.getTypeHandler(propertyType,jdbcType);
if (handler == null || handler instanceof UnknownTypeHandler) {
final int index = columnNames.indexOf(columnName);
final Class<?> javaType = resolveClass(classNames.get(index));
if (javaType != null && jdbcType != null) {
handler = typeHandlerRegistry.getTypeHandler(javaType, jdbcType);
} else if (javaType != null) {
handler = typeHandlerRegistry.getTypeHandler(javaType);
} else if (jdbcType != null) {
handler = typeHandlerRegistry.getTypeHandler(jdbcType);
}
}
if (handler == null || handler instanceof UnknownTypeHandler) {
handler = new ObjectTypeHandler();
}
columnHandlers.put(propertyType, handler);
}
return handler;
}
}
ResultSetHandler是結果集處理器介面
public interface ResultSetHandler {
// 將Statement的執行結果處理為List
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
// 將Statement的執行結果處理為Map
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
// 處理儲存過程的輸出結果
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
DefaultResultSetHandler類作為ResultSetHandler介面的預設也是唯一的實現類
/**
* 處理Statement得到的多結果集(也可能是單結果集,這是多結果集的一種簡化形式),最終得到結果列表
* @param stmt Statement語句
* @return 結果列表
* @throws SQLException
*/
@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;
// 可能會有多個結果集,先取出第一個結果集
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 查詢語句對應的resultMap節點,可能含有多個
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
// 合法性校驗(存在輸出結果集的情況下,resultMapCount不能為0)
validateResultMapsCount(rsw, resultMapCount);
// 迴圈遍歷每一個設定了resultMap的結果集
while (rsw != null && resultMapCount > resultSetCount) {
// 獲得當前結果集對應的resultMap
ResultMap resultMap = resultMaps.get(resultSetCount);
// 進行結果集的處理
handleResultSet(rsw, resultMap, multipleResults, null);
// 獲取下一結果集
rsw = getNextResultSet(stmt);
// 清理上一條結果集的環境
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
// 獲取多結果集中所有結果集的名稱
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
// 迴圈遍歷每一個沒有設定resultMap的結果集
while (rsw != null && resultSetCount < resultSets.length) {
// 獲取該結果集對應的父級resultMap中的resultMapping(注:resultMapping用來描述物件屬性的對映關係)
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
// 獲取被巢狀的resultMap的編號
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
// 處理巢狀對映
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
// 判斷是否是單結果集:如果是則返回結果列表;如果否則返回結果集列表
return collapseSingleResultList(multipleResults);
}
/**
* 處理單一的結果集
* @param rsw ResultSet的包裝
* @param resultMap resultMap節點的資訊
* @param multipleResults 用來儲存處理結果的list
* @param parentMapping
* @throws SQLException
*/
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) { // 巢狀的結果
// 向子方法傳入parentMapping。處理結果中的記錄。
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else { // 非巢狀的結果
if (resultHandler == null) {
// defaultResultHandler能夠將結果物件聚合成一個List返回
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
// 處理結果中的記錄。
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
closeResultSet(rsw.getResultSet());
}
}
/**
* 處理單結果集中的屬性
* @param rsw 單結果集的包裝
* @param resultMap 結果對映
* @param resultHandler 結果處理器
* @param rowBounds 翻頁限制條件
* @param parentMapping 父級結果對映
* @throws SQLException
*/
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);
}
}
/**
* 處理非巢狀對映的結果集
* @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);
// 拿到了一行記錄,並且將其轉化為一個物件
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
// 把這一行記錄轉化出的物件存起來
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
/**
* 儲存當前結果物件
* @param resultHandler 結果處理器
* @param resultContext 結果上下文
* @param rowValue 結果物件
* @param parentMapping 父級結果對映
* @param rs 結果集
* @throws SQLException
*/
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
if (parentMapping != null) {
// 存在父級,則將這一行記錄對應的結果物件繫結到父級結果上
linkToParents(rs, parentMapping, rowValue);
} else {
// 使用resultHandler聚合該物件
callResultHandler(resultHandler, resultContext, rowValue);
}
}
可見在HandleResultSets方法中完成了生成結果物件,為結果物件屬性賦值,將結果物件進行聚合或繫結等重要操作。