相关推荐:mybatis核心组件详解——Executor(未完待续)

概述Executor(org.apache.ibatis.executor.Executor),执行器。publicinterfaceExecutor{ResultHandlerNO_RESULT_HANDLER=null;intupdate(MappedStatementms,Objectparameter)throwsSQLException;<E>List<E>query(MappedStatem

ResultSetHandler(org.apache.ibatis.executor.resultset.ResultSetHandler)结果集处理器:功能定义如下:public interface ResultSetHandler {

<E> List<E> handleResultSets(Statement stmt) throws SQLException;

void handleOutputParameters(CallableStatement cs) throws SQLException;}ResultSetHandler只负责两件事:1.处理Statement执行后产生的结果集,生成结果列表2.处理存储过程执行后的输出参数结果集处理器体系图如下:由图可以看到,mybatis只有唯一一个此接口的默认的实现类,DefaultResultSetHandler(org.apache.ibatis.executor.resultset.DefaultResultSetHandler)。DefaultResultSetHandler是如何完成接口定义的两个功能的呢?首先阐述一下应用场景:通过SQL语句查询出来的数据被封装在ResultSet结果集对象中,可以把它理解为一个二维表,水平是各个字段,竖直是一条条的记录。就算是多表联查,且表与表之间存在一对多或多对多的关系,但是反映在ResultSet中一样是一条一条的记录的形式。如:执行以下SQL(topic表与question表之间是一对多的关系):select a.*,b.question_id,b.question_content from topic a left join question bon 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());

} }

相关推荐:mybatis核心组件详解——MapperAnnotationBuilder

MapperAnnotationBuilder(org.apache.ibatis.builder.annotation.MapperAnnotationBuilder),mapper注解构建器。它的职责很简单,就是解析指定的mapper接口对应的Class对象中,包含的所有mybatis框架中定义的注解,并生成Cache、ResultMap、MappedStatemen

快照源:http://my.oschina.net/lixin91/blog/624772