mybatis核心組件詳解——ResultSetHandler(未完待續)

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來完成的。

再來思考幾個問題:

  1. 針對一個行記錄,其中既包含Topic對象的數據,也包含Question對象的數據,如何才知道應該建立什麼類型的對象?

  2. 若是建立了一個Question對象,如何才能知道它屬於哪一個Topic?

  3. 如何確保不會建立重複的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());
		}
	}
相關文章
相關標籤/搜索