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());
		}
	}
相关文章
相关标签/搜索