标签:imp ash known images pen trie this The 上下文
本文,我们来分享 SQL 执行的第四部分,SQL 执行后,响应的结果集 ResultSet 的处理,涉及 executor/resultset、executor/result、cursor 包。整体类图如下:
老艿艿:在看具体的 DefaultResultSetHandler 的实现代码之前,我们先看看 ResultSetWrapper 的代码。因为 DefaultResultSetHandler 对 ResultSetWrapper 的调用比较多,避免混着解析。
org.apache.ibatis.executor.resultset.ResultSetWrapper ,java.sql.ResultSet 的 包装器,可以理解成 ResultSet 的工具类,提供给 DefaultResultSetHandler 使用。
// ResultSetWrapper.java
|
resultSet 属性,被包装的 ResultSet 对象。columnNames、classNames、jdbcTypes 属性,在 <1> 处,通过遍历 ResultSetMetaData 的字段们,从而解析出来。
// ResultSetWrapper.java
|
<1> 处,先从缓存的 typeHandlerMap 中,获得指定字段名的指定 JavaType 类型的 TypeHandler 对象。<2> 处,如果获取不到,则基于 propertyType + jdbcType 进行查找。其中,#getJdbcType(String columnName) 方法,获得 JdbcType 类型。代码如下:
// ResultSetWrapper.java
|
columnNames 索引到位置 i ,从而到 jdbcTypes 中获得 JdbcType 类型。<3> 处,如果获取不到,则基于 javaType + jdbcType 进行查找。其中,javaType 使用 classNames 中的类型。而 #resolveClass(String className) 方法,获得对应的类。代码如下:
// ResultSetWrapper.java
|
<4> 处,如果获取不到,则使用 ObjectTypeHandler 对象。
<5> 处,缓存 TypeHandler 对象,到 typeHandlerMap 中。#loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) 方法,初始化有 mapped 和无 mapped的字段的名字数组。代码如下:
// ResultSetWrapper.java
|
<1> 处,将 columnPrefix 转换成大写,后调用 #prependPrefixes(Set<String> columnNames, String prefix) 方法,拼接到 resultMap.mappedColumns 属性上。代码如下:
// ResultSetWrapper.java
|
当然,可能有胖友,跟我会懵逼,可能已经忘记什么是 resultMap.mappedColumns 。我们来举个示例:
<resultMap id="B" type="Object">
|
column="year" ,就会被添加到 resultMap.mappedColumns 属性上。<2> 处,遍历 columnNames 数组,根据是否在 mappedColumns 中,分别添加到 mappedColumnNames 和 unmappedColumnNames 中。<3> 处,将 mappedColumnNames 和 unmappedColumnNames 结果,添加到 mappedColumnNamesMap 和 unMappedColumnNamesMap 中。其中,#getMapKey(ResultMap resultMap, String columnPrefix) 方法,获得缓存的 KEY 。代码如下:
// ResultSetWrapper.java
|
下面,我们看个类似的,会调用该方法的方法:
#getMappedColumnNames(ResultMap resultMap, String columnPrefix) 方法,获得有 mapped 的字段的名字的数组。代码如下:
// ResultSetWrapper.java
|
#getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) 方法,获得无 mapped 的字段的名字的数组。代码如下:
// ResultSetWrapper.java
|
?? 具体这两个方法什么用途呢?待到我们在 DefaultResultSetHandler 类里来看。
org.apache.ibatis.executor.resultset.ResultSetHandler ,java.sql.ResultSet 处理器接口。代码如下:
// ResultSetHandler.java
|
老艿艿:保持冷静,DefaultResultSetHandler 有小 1000 行的代码。
org.apache.ibatis.executor.resultset.DefaultResultSetHandler ,实现 ResultSetHandler 接口,默认的 ResultSetHandler 实现类。
// DefaultResultSetHandler.java
|
resultHandler 属性,ResultHandler 对象。用户指定的用于处理结果的处理器,一般情况下,不设置。详细解析,见 「5. ResultHandler」 和 「3.1.2.3.3 storeObject」 。autoMappingsCache 属性,自动映射的缓存。其中,KEY 为 {@link ResultMap#getId()} + ":" + columnPrefix 。详细解析,见 「3.1.2.3.2.4 applyAutomaticMappings」 。#handleResultSets(Statement stmt) 方法,处理 java.sql.ResultSet 结果集,转换成映射的对应结果。代码如下:
// DefaultResultSetHandler.java
|
<1> 处,多 ResultSet 的结果集合,每个 ResultSet 对应一个 Object 对象。而实际上,每个 Object 是 List 对象。在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,multipleResults 最多就一个元素。<2> 处,调用 #getFirstResultSet(Statement stmt) 方法,获得首个 ResultSet 对象,并封装成 ResultSetWrapper 对象。代码如下:
// DefaultResultSetHandler.java
|
<3> 处,调用 MappedStatement#getResultMaps() 方法,获得 ResultMap 数组。在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,resultMaps 就一个元素。
<3.1> 处,调用 #validateResultMapsCount(ResultSetWrapper rsw, int resultMapCount) 方法,校验至少有一个 ResultMap 对象。代码如下:
// DefaultResultSetHandler.java
|
<4.1> 处,获得 ResultMap 对象。<4.2> 处,调用 #handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) 方法,处理 ResultSet ,将结果添加到 multipleResults 中。详细解析,见 「3.1.2.1 handleResultSet」 。<4.3> 处,调用 #getNextResultSet(Statement stmt) 方法,获得下一个 ResultSet 对象,并封装成 ResultSetWrapper 对象。?? 只有存储过程才有多 ResultSet 对象,所以可以忽略。也就是说,实际上,这个 while 循环对我们来说,就不需要啦。<4.4> 处,调用 #cleanUpAfterHandlingResultSet() 方法,执行清理。代码如下:
// DefaultResultSetHandler.java
|
<5> 处,因为 mappedStatement.resultSets 只在存储过程中使用,本系列暂时不考虑,忽略即可。
<6> 处,调用 #collapseSingleResultList(List<Object> multipleResults) 方法,如果是 multipleResults 单元素,则取首元素返回。代码如下:
// DefaultResultSetHandler.java
|
multipleResults.size() 。#handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) 方法,处理 ResultSet ,将结果添加到 multipleResults 中。代码如下:
// DefaultResultSetHandler.java
|
<1> 处,暂时忽略,因为只有存储过程的情况,调用该方法,parentMapping 为非空。<2> 处,如果没有自定义的 resultHandler ,则创建默认的 DefaultResultHandler 对象。<3> 处,调用 #handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) 方法,处理 ResultSet 返回的每一行 Row 。详细解析,见 「3.1.2.2 handleRowValues」 。【特殊】<4> 处,使用默认的 DefaultResultHandler 对象,最终会将 defaultResultHandler 的处理的结果,到 multipleResults 中。而使用自定义的 resultHandler ,不会添加到 multipleResults 中。当然,因为自定义的 resultHandler 对象,是作为一个对象传入,所以在其内部,还是可以存储结果的。例如:
|
<5> 处,调用 #closeResultSet(ResultSet rs) 方法关闭 ResultSet 对象。代码如下:
// DefaultResultSetHandler.java
|
#handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) 方法,处理 ResultSet 返回的每一行 Row 。代码如下:
// DefaultResultSetHandler.java
|
<2> 处理嵌套映射的情况:
<2.1> 处,调用 #handleRowValuesForSimpleResultMap(...) 方法,处理简单映射的结果。详细解析,见 「3.1.2.3 handleRowValuesForSimpleResultMap 简单映射」 。<1> 处理嵌套映射的情况:
<1.1> 处,调用 #ensureNoRowBounds() 方法,校验不要使用 RowBounds 。代码如下:
// DefaultResultSetHandler.java
|
<1.2> 处,调用 #checkResultHandler() 方法,校验不要使用自定义的 resultHandler 。代码如下:
// DefaultResultSetHandler.java
|
<1.3> 处,调用 #handleRowValuesForSimpleResultMap(...) 方法,处理嵌套映射的结果。详细解析,见 「3.1.2.3 handleRowValuesForNestedResultMap 嵌套映射」 。#handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) 方法,处理简单映射的结果。代码如下:
// DefaultResultSetHandler.java
|
<1> 处,创建 DefaultResultContext 对象。详细解析,胖友先跳到 「4. ResultContext」 中,看完就回来。<2> 处,获得 ResultSet 对象,并调用 #skipRows(ResultSet rs, RowBounds rowBounds) 方法,跳到 rowBounds 指定的开始位置。代码如下:
// DefaultResultSetHandler.java
|
org.apache.ibatis.session.RowBounds 类,胖友可以看看 《Mybatis3.3.x技术内幕(十三):Mybatis之RowBounds分页原理》 ,解释的非常不错。<3> 处,循环,满足如下三个条件。其中 #shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) 方法,是否继续处理 ResultSet 。代码如下:
// DefaultResultSetHandler.java
|
<4> 处,调用 #resolveDiscriminatedResultMap(...) 方法,根据该行记录以及 ResultMap.discriminator ,决定映射使用的 ResultMap 对象。详细解析,等下看 「3.1.2.3.1 resolveDiscriminatedResultMap」 。
<5> 处,调用 #getRowValue(...) 方法,根据最终确定的 ResultMap 对 ResultSet 中的该行记录进行映射,得到映射后的结果对象。详细解析,等下看 「3.1.2.3.2 getRowValue」 。<6> 处,调用 #storeObject(...) 方法,将映射创建的结果对象添加到 ResultHandler.resultList 中保存。详细解析,等下看 「3.1.2.3.3 storeObject」 。#resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) 方法,根据该行记录以及 ResultMap.discriminator ,决定映射使用的 ResultMap 对象。代码如下:
// DefaultResultSetHandler.java
|
resultMap ,不会执行这个很复杂的逻辑。?? 所以,如果看不太懂的胖友,可以略过这个方法,问题也不大。代码比较繁杂,胖友跟着注释看看,甚至可以调试下。其中,<1> 处,调用 #getDiscriminatorValue(ResultSet rs, Discriminator discriminator, String columnPrefix) 方法,获得 Discriminator 的指定字段,在 ResultSet 中该字段的值。代码如下:
// DefaultResultSetHandler.java
|
#getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) 方法,根据最终确定的 ResultMap 对 ResultSet 中的该行记录进行映射,得到映射后的结果对象。代码如下:
// DefaultResultSetHandler.java
|
<1> 处,创建 ResultLoaderMap 对象。延迟加载相关。<2> 处,调用 #createResultObject(...) 方法,创建映射后的结果对象。详细解析,见 「3.1.2.3.2.1 createResultObject」 。?? mmp ,这个逻辑的嵌套,真的太深太深了。<3> 处,调用 #hasTypeHandlerForResultObject(rsw, resultMap.getType()) 方法,返回 true ,意味着 rowValue 是基本类型,无需执行下列逻辑。代码如下:
// DefaultResultSetHandler.java
|
BindingTest#shouldInsertAuthorWithSelectKeyAndDynamicParams() 方法。<select resultType="Integer" /> 的情况。<4> 处,创建 MetaObject 对象,用于访问 rowValue 对象。<5> 处,foundValues 代表,是否成功映射任一属性。若成功,则为 true ,若失败,则为 false 。另外,此处使用 useConstructorMappings 作为 foundValues 的初始值,原因是,使用了构造方法创建该结果对象,意味着一定找到了任一属性。<6.1> 处,调用 #shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) 方法,判断是否使用自动映射的功能。代码如下:
// DefaultResultSetHandler.java
|
org.apache.ibatis.session.AutoMappingBehavior ,自动映射行为的枚举。代码如下:
// AutoMappingBehavior.java
|
Configuration.autoMappingBehavior 属性,默认为 AutoMappingBehavior.PARTIAL 。<6.2> 处,调用 #applyAutomaticMappings(...) 方法,自动映射未明确的列。代码有点长,所以,详细解析,见 「3.1.2.3.2.3 applyAutomaticMappings」 。
<7> 处,调用 #applyPropertyMappings(...) 方法,映射 ResultMap 中明确映射的列。代码有点长,所以,详细解析,见 「3.1.2.3.2.4 applyPropertyMappings」 。<8> 处,↑↑↑ 至此,当前 ResultSet 的该行记录的数据,已经完全映射到结果对象 rowValue 的对应属性中。?? 整个过程,非常非常非常长,胖友耐心理解和调试下。<9> 处,如果没有成功映射任意属性,则置空 rowValue 对象。当然,如果开启 configuration.returnInstanceForEmptyRow 属性,则不置空。默认情况下,该值为 false 。#createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) 方法,创建映射后的结果对象。代码如下:
// DefaultResultSetHandler.java
|
<1> 处,useConstructorMappings ,表示是否使用构造方法创建该结果对象。而此处,将其重置为 false 。<2> 处,调用 #createResultObject(...) 方法,创建映射后的结果对象。详细解析,往下看。?? 再次 mmp ,调用链太长了。<3> 处,如果有内嵌的查询,并且开启延迟加载,则调用 ProxyFactory#createProxy(...) 方法,创建结果对象的代理对象。详细解析,见 《精尽 MyBatis 源码分析 —— SQL 执行(五)之延迟加载》 。<4> 处,判断是否使用构造方法创建该结果对象,并设置到 useConstructorMappings 中。#createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) 方法,创建映射后的结果对象。代码如下:
// DefaultResultSetHandler.java
|
<1> 处,情况一,如果有对应的 TypeHandler 对象,则意味着是基本类型,直接创建对结果应对象。调用 #createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) 方法,代码如下:
// DefaultResultSetHandler.java
|
<2> 处,情况二,如果 ResultMap 中,如果定义了 <constructor /> 节点,则通过反射调用该构造方法,创建对应结果对象。调用 #createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) 方法,代码如下:
// DefaultResultSetHandler.java
|
#getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) 方法的逻辑,还是略微比较复杂的。所以,我们在讲完情况三、情况四,我们再来看看它的实现。?? 写到这里,艿艿的心里无比苦闷。详细解析,见 「3.1.2.3.2.2 getNestedQueryConstructorValue」 。<3> 处,情况三,如果有默认的无参的构造方法,则使用该构造方法,创建对应结果对象。<4> 处,情况四,通过自动映射的方式查找合适的构造方法,后使用该构造方法,创建对应结果对象。调用 #createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) 方法,代码如下:
// DefaultResultSetHandler.java
|
<1> 处,获得所有构造方法。<2> 处,调用 #findDefaultConstructor(final Constructor<?>[] constructors) 方法,获得默认构造方法。代码如下:
// DefaultResultSetHandler.java
|
<3> 处,如果有默认构造方法,调用 #createUsingConstructor(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix, Constructor<?> constructor) 方法,使用该构造方法,创建结果对象。代码如下:
// DefaultResultSetHandler.java
|
#createParameterizedResultObject(...) 方法,类似。<4> 处,遍历所有构造方法,调用 #allowedConstructorUsingTypeHandlers(final Constructor<?> constructor, final List<JdbcType> jdbcTypes) 方法,查找符合的构造方法,后创建结果对象。代码如下:
// DefaultResultSetHandler.java
|
老艿艿:冲鸭!!!太冗长了!!!各种各种各种!!!!情况!!!!!
#getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) 方法,获得嵌套查询的值。代码如下:
// DefaultResultSetHandler.java
|
BaseExecutorTest#shouldFetchOneOrphanedPostWithNoBlog() 这个单元测试方法。<1> 处,获得内嵌查询的编号、MappedStatement 对象、参数类型。<2> 处,调用 #prepareParameterForNestedQuery(ResultSet rs, ResultMapping resultMapping, Class<?> parameterType, String columnPrefix) 方法,获得内嵌查询的参数对象。代码如下:
// DefaultResultSetHandler.java
|
① 和 ② 两个序号,分别对应两种情况。<3> 处,整体是,执行查询,获得值。
<3.1> 处,调用 MappedStatement#getBoundSql(Object parameterObject) 方法,获得 BoundSql 对象。<3.2> 处,创建 CacheKey 对象。<3.3> 处,创建 ResultLoader 对象,并调用 ResultLoader#loadResult() 方法,加载结果。详细解析,见 《精尽 MyBatis 源码分析 —— SQL 执行(五)之延迟加载》 。#createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) 方法,创建映射后的结果对象。代码如下:
// DefaultResultSetHandler.java
|
<1> 处,调用 #createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) 方法,获得 UnMappedColumnAutoMapping 数组。代码如下:
// DefaultResultSetHandler.java
|
UnMappedColumnAutoMapping ,是 DefaultResultSetHandler 的内部静态类,未 mapped 字段自动映射后的对象。代码如下:
// DefaultResultSetHandler.java
|
AutoMappingUnknownColumnBehavior#doAction(MappedStatement mappedStatement, String columnName, String propertyName, Class<?> propertyType) 方法,执行相应的逻辑。比较简单,胖友直接看 org.apache.ibatis.session.AutoMappingUnknownColumnBehavior 即可。Configuration.autoMappingUnknownColumnBehavior 为 AutoMappingUnknownColumnBehavior.NONE ,即不处理。<2> 处,遍历 UnMappedColumnAutoMapping 数组,获得指定字段的值,设置到 parameterObject 中,通过 metaObject 。#applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix) 方法,映射 ResultMap 中明确映射的列。代码如下:
// DefaultResultSetHandler.java
|
在 <1> 处,调用 #getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) 方法,获得指定字段的值。代码如下:
// DefaultResultSetHandler.java
|
<2> 处,我们又碰到了一个内嵌查询,调用 #getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) 方法,获得嵌套查询的值。详细解析,见 「」 。#getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) 方法,获得嵌套查询的值。代码如下:
// DefaultResultSetHandler.java
|
#getNestedQueryConstructorValue(...) 方法,用于构造方法需要用到的嵌套查询的值,它是不用考虑延迟加载的。#getNestedQueryMappingValue(...) 方法,用于 setting 方法需要用到的嵌套查询的值,它是需要考虑延迟加载的。<1> 处,调用 Executor#isCached(MappedStatement ms, CacheKey key) 方法,检查缓存中已存在。下面,我们分成两种情况来解析。<2.1> 处,调用 Executor#deferLoad(...) 方法,创建 DeferredLoad 对象,并通过该 DeferredLoad 对象从缓存中加载结采对象。详细解析,见 《精尽 MyBatis 源码分析 —— SQL 执行(五)之延迟加载》 。<2.2> 处,返回已定义 DEFERED 。<3.1> 处,创建 ResultLoader 对象。<3.2> 处,如果要求延迟加载,则延迟加载。
<3.2.1> 处,调用 ResultLoader#addLoader(...) 方法,如果该属性配置了延迟加载,则将其添加到 ResultLoader.loaderMap 中,等待真正使用时再执行嵌套查询并得到结果对象。详细解析,见 《精尽 MyBatis 源码分析 —— SQL 执行(五)之延迟加载》 。<3.3> 处,如果不要求延迟加载,则调用 ResultLoader#loadResult() 方法,直接执行加载对应的值。#storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) 方法,将映射创建的结果对象添加到 ResultHandler.resultList 中保存。代码如下:
// DefaultResultSetHandler.java
|
<x> 处。可能胖友对嵌套映射的概念不是很熟悉,胖友可以调试 AncestorRefTest#testAncestorRef() 这个单元测试方法。
老艿艿:本小节,还是建议看 《MyBatis 技术内幕》 的 「3.3.4 嵌套映射」 小节。因为,它提供了比较好的这块逻辑的原理讲解,并且配置了大量的图。
?? 精力有限,后续补充哈。
?? 实际是,因为艿艿比较少用嵌套映射,所以对这块逻辑,不是很感兴趣。
#handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) 方法,处理嵌套映射的结果。代码如下:
// DefaultResultSetHandler.java
|
#handleCursorResultSets(Statement stmt) 方法,处理 java.sql.ResultSet 成 Cursor 对象。代码如下:
// DefaultResultSetHandler.java
|
老艿艿:这个类,大体看看每个方法的用途,结合上文一起理解即可。
org.apache.ibatis.session.ResultContext ,结果上下文接口。代码如下:
// ResultContext.java
|
org.apache.ibatis.executor.result.DefaultResultContext ,实现 ResultContext 接口,默认的 ResultContext 的实现类。代码如下:
// DefaultResultContext.java
|
org.apache.ibatis.session.ResultHandler ,结果处理器接口。代码如下:
// ResultHandler.java
|
org.apache.ibatis.executor.result.DefaultResultHandler ,实现 ResultHandler 接口,默认的 ResultHandler 的实现类。代码如下:
// DefaultResultHandler.java
|
<1> 处,将当前结果,添加到结果数组中。该类在 session 包中实现,我们放在会话模块的文章中,详细解析。
org.apache.ibatis.cursor.Cursor ,继承 Closeable、Iterable 接口,游标接口。代码如下:
// Cursor.java
|
org.apache.ibatis.cursor.defaults.DefaultCursor ,实现 Cursor 接口,默认 Cursor 实现类。
// DefaultCursor.java
|
CursorStatus ,是 DefaultCursor 的内部枚举类。代码如下:
// DefaultCursor.java
|
// DefaultCursor.java
|
// DefaultCursor.java
|
#iterator() 方法,获取迭代器。代码如下:
// DefaultCursor.java
|
iteratorRetrieved 属性,保证有且仅返回一次 cursorIterator 对象。ObjectWrapperResultHandler ,DefaultCursor 的内部静态类,实现 ResultHandler 接口,代码如下:
// DefaultCursor.java
|
<1> 处,暂存 「3.1 DefaultResultSetHandler」 处理的 ResultSet 的当前行的结果。<2> 处,通过调用 ResultContext#stop() 方法,暂停 DefaultResultSetHandler 在向下遍历下一条记录,从而实现每次在调用 CursorIterator#hasNext() 方法,只遍历一行 ResultSet 的记录。如果胖友有点懵逼,可以在看看 DefaultResultSetHandler#shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) 方法,代码如下:
// DefaultCursor.java
|
CursorIterator ,DefaultCursor 的内部类,实现 Iterator 接口,游标的迭代器实现类。代码如下:
// DefaultCursor.java
|
#hasNext() 方法,判断是否有下一个结果对象:
<1> 处,如果 object 为空,则调用 #fetchNextUsingRowBound() 方法,遍历下一条记录。也就是说,该方法是先获取下一条记录,然后在判断是否存在下一条记录。实际上,和 java.util.ResultSet 的方式,是一致的。如果再调用一次该方法,则不会去遍历下一条记录。关于 #fetchNextUsingRowBound() 方法,详细解析,见 「6.1.8 fetchNextUsingRowBound」 。<2> 处,判断 object 非空。#next() 方法,获得下一个结果对象:
<3> 处,先记录 object 到 next 中。为什么要这么做呢?继续往下看。<4> 处,如果 next 为空,有两种可能性:1)使用方未调用 #hasNext() 方法;2)调用 #hasNext() 方法,发现没下一条,还是调用了 #next() 方法。如果 next() 方法为空,通过“再次”调用 #fetchNextUsingRowBound() 方法,去遍历下一条记录。<5> 处,如果 next 非空,说明有记录,则进行返回。
<5.1> 处,置空 object 对象。<5.2> 处,增加 iteratorIndex 。<5.3> 处,返回 next 。如果 <3> 处,不进行 next 的赋值,如果 <5.1> 处的置空,此处就无法返回 next 了。<6> 处,如果 next 为空,说明没有记录,抛出 NoSuchElementException 异常。#fetchNextUsingRowBound() 方法,遍历下一条记录。代码如下:
// DefaultCursor.java
|
<1> 处,调用 #fetchNextObjectFromDatabase() 方法,遍历下一条记录。代码如下:
// DefaultCursor.java
|
<1> 处,调用 #isClosed() 方法,判断是否已经关闭。若是,则返回 null 。代码如下:
// DefaultCursor.java
|
<2> 处,设置状态为 CursorStatus.OPEN 。
<3> 处,调用 DefaultResultSetHandler#handleRowValues(...) 方法,遍历下一条记录。也就是说,回到了 [「3.1.2.2 handleRowValues」 的流程。遍历的下一条件记录,会暂存到 objectWrapperResultHandler.result 中。<4> 处,复制给 next 。<5> 处,增加 indexWithRowBound 。<6> 处,没有更多记录,或者到达 rowBounds 的限制索引位置,则关闭游标,并设置状态为 CursorStatus.CONSUMED 。其中,涉及到的方法,代码如下:
// DefaultCursor.java
|
<7> 处,置空 objectWrapperResultHandler.result 属性。<8> 处,返回 next 。标签:imp ash known images pen trie this The 上下文
原文地址:https://www.cnblogs.com/siye1989/p/11624230.html