mybatis中ResultSetHandler的设计与实现
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的设计与实现相关推荐
- mybatis中 Executor的设计与实现
Executor的接口定义主要包含sql操作,事务.缓存操作 其基于模板方法模式设计的 其类层次关系图为 1.Executor 1.1 BaseExecutor 是SimpleExecutor,Reu ...
- mybatis中statementHandler的设计与实现
1.StatementHandler StatementHandler接口定义包含创建Statement, 绑定参数,批量执行,更新,查询,获取参数处理器 类结构图为 PreparedStatemen ...
- mybatis中的mapper设计与原理
mapper是基于动态代理来设计的,其类图如下 在MapperRegistry添加mapper时,会基于注解作解析 public <T> void addMapper(Class<T ...
- mybatis中sqlSession的设计与实现
sqlSession基于工厂方法来实现的,SqlSession和SqlSessionFactory的接口定义如下 public interface SqlSession extends Closeab ...
- mybatis中缓存的设计与原理
缓存是基于装饰器设计模式来设计的,接口为Cache,实现类为PerpetualCache,具体的装饰器有基于淘汰策略的.对象引用类型的.序列化的.事务的.同步的.日志的(记录缓存命中率).时间调度的. ...
- mybatis中的TypeHandler设计与实现
TypeHandler主要是用在从java数据写入数据库时,从数据库中读取数据时的从java到jdbc类型之间的转换. 其类层次图为 TypeHandler:主要定义了设置参数.根据列名获取参数,列索 ...
- Mybatis中SqlSession下的四大核心组件分析
SqlSession下的四大核心组件 Mybatis中SqlSession下的四大核心组件:ParameterHandler .ResultSetHandler .StatementHandler . ...
- MyBatis 中的九种设计模式
点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | http://www.crazyant.net ...
- 《深入理解mybatis原理》 MyBatis缓存机制的设计与实现
本文主要讲解MyBatis非常棒的缓存机制的设计原理,给读者们介绍一下MyBatis的缓存机制的轮廓,然后会分别针对缓存机制中的方方面面展开讨论. MyBatis将数据缓存设计成两级结构,分为一级缓存 ...
最新文章
- MATLAB中如何将一幅图像的地理信息写入另一幅图像
- SpringBoot + Mybatis + Druid + PageHelper 实现多数据源分页
- linux如何过滤字符串,在linux系统如何grep过滤中,不包含某些字符串的命令
- Python 并行编程
- 阅读《平庸的世界》后感觉 (两)
- (转)RabbitMQ学习之安装
- Ubuntu16.04 安装Maven 3.5.3
- 一篇文章了解JsBridge之IOS篇
- 14. model(2)
- printf()、sprintf()的二次封装
- 绝对优势与比较优势的数学分析与其…
- 如何像打王者荣耀一样励志学习
- Tomcat服务器日志输出格式设置
- 使用Go语言完成文件夹的MD5计算
- 微信小程序面试题大全
- 现如今99%的区块链都是空气项目
- vsftpd 升级3.0.2-29 和 增加账号访问
- 关于微信公众号页面获取code进行微信授权登录
- zlg72128数码管驱动管理显示芯片
- 【STM32F429】第13章 任务调度—抢占式,时间片和合作式
热门文章
- redis搭建与安装2
- linux下sudo命令[转]
- 【leetcode】Best Time to Buy and Sell Stock
- python中文编码-Python中文乱码(转)
- vscode使用教程python-如何在VSCode上轻松舒适的配置Python的方法步骤
- python常用内置函数总结-Python 常用内置函数
- python3.8.5 应用程序无法启动-macos python3.8.5 打开摄像头问题
- python人工智能-Python在人工智能中的作用
- python编程培训多少钱-线下python培训要多少钱?
- python资料百度网盘-python自动保存百度盘资源到百度盘中的实例代码