标签: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