ResultSetHandler(org.apache.ibatis.executor.resultset.ResultSetHandler)結果集處理器:java
功能定義以下:sql
public interface ResultSetHandler { <E> List<E> handleResultSets(Statement stmt) throws SQLException; void handleOutputParameters(CallableStatement cs) throws SQLException; }
ResultSetHandler只負責兩件事:apache
1.處理Statement執行後產生的結果集,生成結果列表緩存
2.處理存儲過程執行後的輸出參數mybatis
結果集處理器體系圖以下:app
由圖能夠看到,mybatis只有惟一一個此接口的默認的實現類,DefaultResultSetHandler(org.apache.ibatis.executor.resultset.DefaultResultSetHandler)。spa
DefaultResultSetHandler是如何完成接口定義的兩個功能的呢?code
首先闡述一下應用場景:對象
經過SQL語句查詢出來的數據被封裝在ResultSet結果集對象中,能夠把它理解爲一個二維表,水平是各個字段,豎直是一條條的記錄。就算是多表聯查,且表與表之間存在一對多或多對多的關係,可是反映在ResultSet中同樣是一條一條的記錄的形式。如:索引
執行如下SQL(topic表與question表之間是一對多的關係):
select a.*,b.question_id,b.question_content from topic a left join question b on a.topic_id=b.topic_id
得到結果集以下:
原本話題表中只有4個話題,卻被展現成了5條記錄,由於有兩個問題是屬於同一個話題的
在javaBean中保存數據時卻不盡相同。由於對象是屬性值的集合,屬性值自己又能夠是對象類型,所以對象自己是存在嵌套層次的,它能夠很好地封裝不一樣表中記錄之間的關係。以下:
public class Topic { private Integer id; private String content; // 關聯關係 private Category category; private List<Question> questionList; }
public class Question { private Integer id; private String content; private Integer topicId; // 關聯關係 private Topic topic; }
在mybatis執行SQL返回的List<Topic>中,只有4個topic對象。ResultSet結果集數據到具備嵌套層級結構的beanList之間的轉換,就是由DefaultResultSetHandler來完成的。
再來思考幾個問題:
針對一個行記錄,其中既包含Topic對象的數據,也包含Question對象的數據,如何才知道應該建立什麼類型的對象?
若是建立了一個Question對象,如何才能知道它屬於哪一個Topic?
如何確保不會建立重複的Topic對象?
DefaultResultSetHandler是經過ResultMap對象(結果映射關係集合)肯定應該建立什麼類型的對象,以及如何注入數據的。ResultMap具備嵌套結構,ResultMap是ResultMapping對象的集合,ResultMapping對象自己多是含有ResultMap的複雜映射,也多是一個字段對應一個屬性值的簡單映射。咱們能夠認爲,不管當前處於嵌套結構的哪一層,調用getRowValue總能夠根據ResultMap和ResultSet生成一個rowValue對象。至於這個rowValue如何處理,是該添加到另外一個對象中,仍是它自己就是一個獨立的對象,這個由storeObject()決定。
DefaultResultSetHandler給全部的rowValue創建了索引,索引的Key是CacheKey(org.apache.ibatis.cache.CacheKey)類型的。CacheKey根據ResultMap的id,和ResultMap中全部具備ID標誌的ResultMapping中的column,以及column對應在ResultSet中當前行記錄中的值生成,它還包括rowValue的層級信息。用索引的方式,能夠確保不建立重複的對象,一級肯定建立的rowValue該如何處理。
源碼詳解:
DefaultResultSetHandler索引結構以下:
// 此Map用來保存當前層級內部的結果對象(一對多關係的多方對象),key爲combinedKey private final Map<CacheKey, Object> nestedResultObjects = new HashMap<CacheKey, Object>(); // 此Map用來保存當前層級的根對象(一對多關係中的一方對象),key爲absoluteKey private final Map<CacheKey, Object> ancestorObjects = new HashMap<CacheKey, Object>();
爲方便讀者理解層級的概念,舉例以下:
設有三個類:
public class Category { private Integer categoryId; private String categoryContent; // 關聯關係 private List<Topic> topicList; }
public class Topic { private Integer topicId; private String topicContent; // 關聯關係 private Category category; private List<Question> questionList; }
public class Question { private Integer questionId; private String questionContent; // 關聯關係 private Topic topic; }
當解析生成Category對象時,會把Category對象加入ancestorObjects中,把其內部的Topic對象加入nestedResultObjects中
當解析生成Topic對象時,會把Topic對象加入ancestorObjects中,把其內部的Question對象和Category對象加入nestedResultObjects中
當解析生成Question對象時,會把Question對象加入ancestorObjects中,把其內部的Topic對象加入nestedResultObjects中
層級的概念講清楚了,下面看一下DefaultResultSetHandler執行結果映射的具體邏輯。
DefaultResultSetHandler分爲handleRowValuesForSimpleResultMap簡單結果映射和handleRowValuesForNestedResultMap嵌套結果映射兩種。
1.嵌套結果映射邏輯以下:
private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException { // 建立默認結果上下文 final DefaultResultContext resultContext = new DefaultResultContext(); // 跳過rowBounds指定offset行偏移量 skipRows(rsw.getResultSet(), rowBounds); Object rowValue = null; // 如何定義應該處理:上下文沒有主動中止,結果集還有記錄,且上下文中結果對象數量不足時,應該繼續處理 while (shouldProcessMoreRows(rsw.getResultSet(), resultContext, rowBounds)) { // 解決鑑別過的結果映射,邏輯以下: // 獲取結果映射中的鑑別器,經過鑑別指定字段經過配置對象獲取對應的另外一個結果映射,循環往復, // 直到找不到鑑別器爲止,返回最終的結果映射 final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null); // 建立緩存key,如何建立? final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null); // 緩存中獲取結果對象 Object partialObject = nestedResultObjects.get(rowKey); // if (mappedStatement.isResultOrdered()) { // issue #577 && #542 if (partialObject == null && rowValue != null) { nestedResultObjects.clear(); // 保存結果對象 storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()); } rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, rowKey, null, partialObject); } else { // 獲取行值 rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, rowKey, null, partialObject); if (partialObject == null) { storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()); } } } if (rowValue != null && mappedStatement.isResultOrdered()) { storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()); } }