ResultSetHandler主要处理statement,cursor,callstatement三种类型的结果集,其接口定义如下:

public interface ResultSetHandler {<E> List<E> handleResultSets(Statement stmt) throws SQLException;<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;void handleOutputParameters(CallableStatement cs) throws SQLException;}

在介绍ResultSetHandler之前,先看下与ResultSet相关的一个类,ResultSetWrapper,其包含的成员有ResultSet,列名,及对应的jdbcType和javaType,其根据ResultMap的定义及结果集,得到映射和没有映射的列名集合

public List<String> getMappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {List<String> mappedColumnNames = mappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));if (mappedColumnNames == null) {loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);mappedColumnNames = mappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));}return mappedColumnNames;}private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {List<String> mappedColumnNames = new ArrayList<String>();List<String> unmappedColumnNames = new ArrayList<String>();final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);for (String columnName : columnNames) {final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);if (mappedColumns.contains(upperColumnName)) {mappedColumnNames.add(upperColumnName);} else {unmappedColumnNames.add(columnName);}}mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);}

1、DefaultRecordSetHandler对RecordSet的处理

其是mybatis中ResultSetHandler的唯一默认实现。

public List<Object> handleResultSets(Statement stmt) throws SQLException {ErrorContext.instance().activity("handling results").object(mappedStatement.getId());final List<Object> multipleResults = new ArrayList<Object>();int resultSetCount = 0;//获取第一个结果集,可能有多个(对于存储过程)ResultSetWrapper rsw = getFirstResultSet(stmt);//对于多结果集的,可以配置多个resultMapList<ResultMap> resultMaps = mappedStatement.getResultMaps();int resultMapCount = resultMaps.size();validateResultMapsCount(rsw, resultMapCount);while (rsw != null && resultMapCount > resultSetCount) {ResultMap resultMap = resultMaps.get(resultSetCount);//通过resultMap与recordSet映射handleResultSet(rsw, resultMap, multipleResults, null);rsw = getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount++;}String[] resultSets = mappedStatement.getResultSets();if (resultSets != null) {while (rsw != null && resultSetCount < resultSets.length) {ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);if (parentMapping != null) {String nestedResultMapId = parentMapping.getNestedResultMapId();ResultMap resultMap = configuration.getResultMap(nestedResultMapId);handleResultSet(rsw, resultMap, null, parentMapping);}rsw = getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount++;}}return collapseSingleResultList(multipleResults);}

单独处理recordSet是通过handleResultSet来实现的.

当配置的是多个recordsets,而只对应一个resultMap,但是resultMap中的其中一个映射到其中的一个recordSet时,会进入到第一个分支。

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {try {if (parentMapping != null) {handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);} else {if (resultHandler == null) {DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);multipleResults.add(defaultResultHandler.getResultList());} else {handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);}}} finally {// issue #228 (close resultsets)closeResultSet(rsw.getResultSet());}}

处理分为简单映射和嵌套映射

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {if (resultMap.hasNestedResultMaps()) {ensureNoRowBounds();checkResultHandler();handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);} else {handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);}}

简单映射的处理流程为

嵌套映射的处理流程为:前面与简单映射一样,跳到偏移所在的行,在没有到limit时,先根据discriminate找到ResultMap,如果有IdResultMapping就用此来创建CacheKey,否则使用 PropertyResultMapping来创建CacheKey。在嵌套结果对象没有时,创建metaobject,首先根据构造函数设置,根据是否需要自动映射来映射没有指定映射的列,然后映射指定的属性,接着作嵌套映射,在有嵌套结果对象时,直接作嵌套映射,最后调用callResultHandler存储对象

private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {final DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();skipRows(rsw.getResultSet(), rowBounds);Object rowValue = previousRowValue;while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);Object partialObject = nestedResultObjects.get(rowKey);// issue #577 && #542if (mappedStatement.isResultOrdered()) {if (partialObject == null && rowValue != null) {nestedResultObjects.clear();storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());}rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);} else {rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);if (partialObject == null) {storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());}}}if (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds)) {storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());previousRowValue = null;} else if (rowValue != null) {previousRowValue = rowValue;}}

代理工厂设计的类层次图为

多结果集的处理

private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)throws SQLException {if (propertyMapping.getNestedQueryId() != null) {return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);} else if (propertyMapping.getResultSet() != null) {addPendingChildRelation(rs, metaResultObject, propertyMapping);   // TODO is that OK?return DEFERED;} else {final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);return typeHandler.getResult(rs, column);}}private void addPendingChildRelation(ResultSet rs, MetaObject metaResultObject, ResultMapping parentMapping) throws SQLException {CacheKey cacheKey = createKeyForMultipleResults(rs, parentMapping, parentMapping.getColumn(), parentMapping.getColumn());PendingRelation deferLoad = new PendingRelation();deferLoad.metaObject = metaResultObject;deferLoad.propertyMapping = parentMapping;List<PendingRelation> relations = pendingRelations.get(cacheKey);// issue #255if (relations == null) {relations = new ArrayList<DefaultResultSetHandler.PendingRelation>();pendingRelations.put(cacheKey, relations);}relations.add(deferLoad);ResultMapping previous = nextResultMaps.get(parentMapping.getResultSet());if (previous == null) {nextResultMaps.put(parentMapping.getResultSet(), parentMapping);} else {if (!previous.equals(parentMapping)) {throw new ExecutorException("Two different properties are mapped to the same resultSet");}}}

2、对Cursor的处理

其使用迭代器设计模式,基于DefaultCursor来处理游标的。使用内部类CursorIterator来作迭代,其类关系图为

public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException {ErrorContext.instance().activity("handling cursor results").object(mappedStatement.getId());ResultSetWrapper rsw = getFirstResultSet(stmt);List<ResultMap> resultMaps = mappedStatement.getResultMaps();int resultMapCount = resultMaps.size();validateResultMapsCount(rsw, resultMapCount);if (resultMapCount != 1) {throw new ExecutorException("Cursor results cannot be mapped to multiple resultMaps");}ResultMap resultMap = resultMaps.get(0);return new DefaultCursor<E>(this, resultMap, rsw, rowBounds);}//CursorIterator@Overridepublic boolean hasNext() {if (object == null) {object = fetchNextUsingRowBound();}return object != null;}protected T fetchNextUsingRowBound() {T result = fetchNextObjectFromDatabase();while (result != null && indexWithRowBound < rowBounds.getOffset()) {result = fetchNextObjectFromDatabase();}return result;}protected T fetchNextObjectFromDatabase() {if (isClosed()) {return null;}try {status = CursorStatus.OPEN;resultSetHandler.handleRowValues(rsw, resultMap, objectWrapperResultHandler, RowBounds.DEFAULT, null);} catch (SQLException e) {throw new RuntimeException(e);}T next = objectWrapperResultHandler.result;if (next != null) {indexWithRowBound++;}// No more object or limit reachedif (next == null || (getReadItemsCount() == rowBounds.getOffset() + rowBounds.getLimit())) {close();status = CursorStatus.CONSUMED;}objectWrapperResultHandler.result = null;return next;}public T next() {// Fill next with object fetched from hasNext()T next = object;if (next == null) {next = fetchNextUsingRowBound();}if (next != null) {object = null;iteratorIndex++;return next;}throw new NoSuchElementException();}

3、对存储过程的处理

对绑定的sql的参数映射处理,参数类型为out或者inout类型。

public void handleOutputParameters(CallableStatement cs) throws SQLException {final Object parameterObject = parameterHandler.getParameterObject();final MetaObject metaParam = configuration.newMetaObject(parameterObject);final List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();for (int i = 0; i < parameterMappings.size(); i++) {final ParameterMapping parameterMapping = parameterMappings.get(i);if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {if (ResultSet.class.equals(parameterMapping.getJavaType())) {handleRefCursorOutputParameter((ResultSet) cs.getObject(i + 1), parameterMapping, metaParam);} else {final TypeHandler<?> typeHandler = parameterMapping.getTypeHandler();metaParam.setValue(parameterMapping.getProperty(), typeHandler.getResult(cs, i + 1));}}}}private void handleRefCursorOutputParameter(ResultSet rs, ParameterMapping parameterMapping, MetaObject metaParam) throws SQLException {if (rs == null) {return;}try {final String resultMapId = parameterMapping.getResultMapId();final ResultMap resultMap = configuration.getResultMap(resultMapId);final DefaultResultHandler resultHandler = new DefaultResultHandler(objectFactory);final ResultSetWrapper rsw = new ResultSetWrapper(rs, configuration);handleRowValues(rsw, resultMap, resultHandler, new RowBounds(), null);metaParam.setValue(parameterMapping.getProperty(), resultHandler.getResultList());} finally {// issue #228 (close resultsets)closeResultSet(rs);}}

mybatis中ResultSetHandler的设计与实现相关推荐

  1. mybatis中 Executor的设计与实现

    Executor的接口定义主要包含sql操作,事务.缓存操作 其基于模板方法模式设计的 其类层次关系图为 1.Executor 1.1 BaseExecutor 是SimpleExecutor,Reu ...

  2. mybatis中statementHandler的设计与实现

    1.StatementHandler StatementHandler接口定义包含创建Statement, 绑定参数,批量执行,更新,查询,获取参数处理器 类结构图为 PreparedStatemen ...

  3. mybatis中的mapper设计与原理

    mapper是基于动态代理来设计的,其类图如下 在MapperRegistry添加mapper时,会基于注解作解析 public <T> void addMapper(Class<T ...

  4. mybatis中sqlSession的设计与实现

    sqlSession基于工厂方法来实现的,SqlSession和SqlSessionFactory的接口定义如下 public interface SqlSession extends Closeab ...

  5. mybatis中缓存的设计与原理

    缓存是基于装饰器设计模式来设计的,接口为Cache,实现类为PerpetualCache,具体的装饰器有基于淘汰策略的.对象引用类型的.序列化的.事务的.同步的.日志的(记录缓存命中率).时间调度的. ...

  6. mybatis中的TypeHandler设计与实现

    TypeHandler主要是用在从java数据写入数据库时,从数据库中读取数据时的从java到jdbc类型之间的转换. 其类层次图为 TypeHandler:主要定义了设置参数.根据列名获取参数,列索 ...

  7. Mybatis中SqlSession下的四大核心组件分析

    SqlSession下的四大核心组件 Mybatis中SqlSession下的四大核心组件:ParameterHandler .ResultSetHandler .StatementHandler . ...

  8. MyBatis 中的九种设计模式

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | http://www.crazyant.net ...

  9. 《深入理解mybatis原理》 MyBatis缓存机制的设计与实现

    本文主要讲解MyBatis非常棒的缓存机制的设计原理,给读者们介绍一下MyBatis的缓存机制的轮廓,然后会分别针对缓存机制中的方方面面展开讨论. MyBatis将数据缓存设计成两级结构,分为一级缓存 ...

最新文章

  1. MATLAB中如何将一幅图像的地理信息写入另一幅图像
  2. SpringBoot + Mybatis + Druid + PageHelper 实现多数据源分页
  3. linux如何过滤字符串,在linux系统如何grep过滤中,不包含某些字符串的命令
  4. Python 并行编程
  5. 阅读《平庸的世界》后感觉 (两)
  6. (转)RabbitMQ学习之安装
  7. Ubuntu16.04 安装Maven 3.5.3
  8. 一篇文章了解JsBridge之IOS篇
  9. 14. model(2)
  10. printf()、sprintf()的二次封装
  11. 绝对优势与比较优势的数学分析与其…
  12. 如何像打王者荣耀一样励志学习
  13. Tomcat服务器日志输出格式设置
  14. 使用Go语言完成文件夹的MD5计算
  15. 微信小程序面试题大全
  16. 现如今99%的区块链都是空气项目
  17. vsftpd 升级3.0.2-29 和 增加账号访问
  18. 关于微信公众号页面获取code进行微信授权登录
  19. zlg72128数码管驱动管理显示芯片
  20. 【STM32F429】第13章 任务调度—抢占式,时间片和合作式

热门文章

  1. redis搭建与安装2
  2. linux下sudo命令[转]
  3. 【leetcode】Best Time to Buy and Sell Stock
  4. python中文编码-Python中文乱码(转)
  5. vscode使用教程python-如何在VSCode上轻松舒适的配置Python的方法步骤
  6. python常用内置函数总结-Python 常用内置函数
  7. python3.8.5 应用程序无法启动-macos python3.8.5 打开摄像头问题
  8. python人工智能-Python在人工智能中的作用
  9. python编程培训多少钱-线下python培训要多少钱?
  10. python资料百度网盘-python自动保存百度盘资源到百度盘中的实例代码