我们之前介绍过了MyBatis 四大核心配置之 Executor、StatementHandler、 ParameterHandler,今天本文的主题是介绍一下 MyBatis 最后一个神器也就是 ResultSetHandler。那么开始我们的讨论

ResultSetHandler 简介

回想一下,一条 SQL 的请求过程会经过哪几个步骤? 首先会经过 Executor 执行器,它主要负责管理创建 StatementHandler 对象,然后由 StatementHandler 对象做数据库的连接以及生成 Statement 对象,并解析 SQL 参数,由 ParameterHandler 对象负责把 Mapper 方法中的参数映射到 XML 中的 SQL 语句中,那么是不是还少了一个步骤,就能完成一个完整的 SQL 请求了?没错,这最后一步就是 SQL 结果集的处理工作,也就是 ResultSetHandler 的主要工作

要了解 ResultSetHandler 之前,首先需要了解 ResultSetHandler的继承关系以及基本方法

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是一个接口,它只有一个默认的实现类,像是 ParameterHandler 一样,它的默认实现类是DefaultResultSetHandler

ResultSetHandler 创建

ResultSetHandler 是在处理查询请求的时候由 Configuration 对象负责创建,示例如下

protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds; this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory(); if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
} this.boundSql = boundSql; // 创建参数处理器
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
// 创建结果映射器
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
} public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
// 由 DefaultResultSetHandler 进行初始化
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}

上述的创建过程是对 ResultSetHandler 创建过程以及初始化的简单解释,下面是对具体的查询请求进行分析

ResultSetHandler 处理结果映射

回想一下,我们在进行传统crud操作的时候,哪些方法是需要返回值的?当然我们说的返回值指的是从数据库中查询出来的值,而不是标识符,应该只有查询方法吧?所以 MyBatis 只针对 query 方法做了返回值的映射,代码如下:

PreparedStatementHandler.java

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
// 处理结果集
return resultSetHandler.<E> handleResultSets(ps);
} @Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
// 批量处理结果集
return resultSetHandler.<E> handleCursorResultSets(ps);
}

CallableStatementHandler.java 处理存储过程的SQL

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
cs.execute();
List<E> resultList = resultSetHandler.<E>handleResultSets(cs);
resultSetHandler.handleOutputParameters(cs);
return resultList;
} @Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
cs.execute();
Cursor<E> resultList = resultSetHandler.<E>handleCursorResultSets(cs);
resultSetHandler.handleOutputParameters(cs);
return resultList;
}

DefaultResultSetHandler 源码解析

MyBatis 只有一个默认的实现类就是 DefaultResultSetHandler,ResultSetHandler 主要负责处理两件事

  1. 处理 Statement 执行后产生的结果集,生成结果列表
  2. 处理存储过程执行后的输出参数

按照 Mapper 文件中配置的 ResultType 或 ResultMap 来封装成对应的对象,最后将封装的对象返回即可。

来看一下主要的源码:

@Override
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);
// 获取结果映射
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
// 结果映射的大小
int resultMapCount = resultMaps.size();
// 校验结果映射的数量
validateResultMapsCount(rsw, resultMapCount);
// 如果ResultSet 包装器不是null, 并且 resultmap 的数量 > resultSet 的数量的话
// 因为 resultSetCount 第一次肯定是0,所以直接判断 ResultSetWrapper 是否为 0 即可
while (rsw != null && resultMapCount > resultSetCount) {
// 从 resultMap 中取出 resultSet 数量
ResultMap resultMap = resultMaps.get(resultSetCount);
// 处理结果集, 关闭结果集
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
} // 从 mappedStatement 取出结果集
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);
}

其中涉及的主要对象有:

ResultSetWrapper : 结果集的包装器,主要针对结果集进行的一层包装,它的主要属性有

  • ResultSet : Java JDBC ResultSet接口表示数据库查询的结果。 有关查询的文本显示了如何将查询结果作为java.sql.ResultSet返回。 然后迭代此ResultSet以检查结果。
  • TypeHandlerRegistry: 类型注册器,TypeHandlerRegistry 在初始化的时候会把所有的 Java类型和类型转换器进行注册。
  • ColumnNames: 字段的名称,也就是查询操作需要返回的字段名称
  • ClassNames: 字段的类型名称,也就是 ColumnNames 每个字段名称的类型
  • JdbcTypes: JDBC 的类型,也就是java.sql.Types 类型

ResultMap: 负责处理更复杂的映射关系

multipleResults:

其中的主要方法是 handleResultSet

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());
}
} // 如果有嵌套的ResultMap 的话
// 确保没有行绑定
// 检查结果处理器
// 如果没有的话,直接处理简单的ResultMap
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);
}
}

handleResultSets 方法返回的是 collapseSingleResultList(multipleResults) ,它是什么呢?

private List<Object> collapseSingleResultList(List<Object> multipleResults) {
return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
}

它是判断的 multipleResults 的数量,如果数量是 1 ,就直接取位置为0的元素,如果不是1,那就返回 multipleResults 的真实数量

那么 multipleResults 的数量是哪来的呢?

它的值其实是处理结果集中传递进去的

handleResultSet(rsw, resultMap, multipleResults, null);

然后在处理结果集的方法中对 multipleResults 进行添加

multipleResults.add(defaultResultHandler.getResultList());

下面我们来看一下返回的真实实现类 DefaultResultSetHandler 中的结构组成

在 DefaultResultSetHandler 中处理完结果映射,并把上述结构返回给调用的客户端,从而执行完成一条完整的SQL语句。

我的公众号二维码:欢迎关注

文章参考:

https://blog.csdn.net/qq924862077/article/details/52704191

MyBatis 核心配置综述之 ResultSetHandler的更多相关文章

  1. MyBatis 核心配置综述之StatementHandler

    目录 MyBatis 核心配置综述之StatementHandler MyBatis 四大组件之StatementHandler StatementHandler 的基本构成 StatementHan ...

  2. MyBatis 核心配置综述之 ParameterHandler

    目录 ParameterHandler 简介 ParameterHandler 创建 ParameterHandler 中的参数从何而来 ParameterHandler 解析 MyBatis 四大核 ...

  3. MyBatis 核心配置综述之Executor

    目录 MyBatis四大组件之 Executor执行器 Executor的继承结构 Executor创建过程以及源码分析 Executor接口的主要方法 Executor 的现实抽象 上一篇我们对Sq ...

  4. 1.2(Mybatis学习笔记)Mybatis核心配置

    一.Mybatis核心对象 1.1SqlSeesionFactory SqlSessionFactory主要作用是创建时SqlSession. SqlSessionFactory可通过SqlSessi ...

  5. Mybatis学习——Mybatis核心配置

    MyBatis的核心配置 在使用MyBatis框架时,设计两个核心的d对象:SqlSessionFactory和SqlSession. SqlsessionFactory SqlSessionFact ...

  6. Mybatis的核心配置

    之前了解了Mybatis的基本用法,现在学习一下Mybatis框架中的核心对象以及映射文件和配置文件,来深入的了解这个框架. 1.Mybatis的核心对象 使用MyBatis框架时,主要涉及两个核心对 ...

  7. 笔记:MyBatis XML配置详解

    MyBatis 的配置文件包含了影响 MyBatis 行为甚深的设置(settings)和属性(properties)信息.文档的顶层结构如下: configuration 配置 properties ...

  8. MyBatis核心配置文件详解

    ------------------------siwuxie095                                     MyBatis 核心配置文件详解         1.核心 ...

  9. Java-MyBatis:MyBatis 3 配置

    ylbtech-Java-MyBatis:MyBatis 3 配置 1.返回顶部 1. XML 映射配置文件 MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置(settings) ...

随机推荐

  1. 随时发布:REST API文档的代码仓库中的持续集成与协作

    本文主要内容:API文档提供了预测客户成功的关键路径:在代码附近的文档上进行协作可以更好地检查代码和文档文件,提高自动化效率,并专门针对文档进行质量测试:提供通用文档框架,标准,自动化和工具,以提高团 ...

  2. iOS中 分类(category)与扩展(Extension)的区别?

    1.分类(category)的作用 (1).作用:可以在不修改原来类的基础上,为一个类扩展方法.(2).最主要的用法:给系统自带的类扩展方法. 2.分类中能写点啥? (1).分类中只能添加“方法”,不 ...

  3. 并发编程-concurrent指南-阻塞队列-延迟队列DelayQueue

    DelayQueue是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走.这种队列是有序的,即队头对象的延迟到期时间最长.注意:不能 ...

  4. Codeforces Gym101518F:Dimensional Warp Drive(二分+高斯消元)

    题目链接 题意 给出一个11元组A和11元组B,给出n个11元方程,每个方程有一个日期,要让A变成B,问最少需要日期多少才可以变. 思路 因为日期满足单调性,所以可以二分答案.判断的时候就是高斯消元套 ...

  5. 图解AQS原理之ReentrantLock详解-非公平锁

    概述 并发编程中,ReentrantLock的使用是比较多的,包括之前讲的LinkedBlockingQueue和ArrayBlockQueue的内部都是使用的ReentrantLock,谈到它又不能 ...

  6. Coder必须自废的两样神功

    Coder必须自废的两样神功 大理段氏以一阳指神功驰名天下.奉六脉宝经,立天龙佛院:凭借数百载基业威名,与嵩山少林.终南全真分庭抗礼:乃宋代中华武术三大派系之一. 二指禅是一种鲜为人知的秘传功法,通过 ...

  7. I/O:OutputStream

    OutputStream: void close() :关闭此输出流并释放与此流有关的所有系统资源. void flush() :刷新此输出流并强制写出所有缓冲的输出字节. void write(by ...

  8. 简单的量子算法(一):Hadamard 变换、Parity Problem

    Hadamard Transform Hadamard 变换在量子逻辑门中提过,只不过那时是单量子的Hadamard门,负责把\(|1\rangle\)变成\(|-\rangle\),\(|0\ran ...

  9. 个人永久性免费-Excel催化剂功能第52波-相同内容批量合并单元格,取消合并单元格并填充内容

    在高级Excel用户群体中无比痛恨的合并单元格,在现实的表格中却阴魂不散的纠缠不断.今天Excel催化剂也来成为“帮凶”,制造更多的合并单元格.虽然开发出此功能,请使用过程中务必要保持节制,在可以称为 ...

  10. MySql的数据库优化到底优啥了都??(2)

    嘟嘟在写此篇文章之前心里先默念(简单,通俗,工整)*10 吟唱完了,进入正题 3.Memory存储引擎 除了存放一个表结构相关信息的.frm文件在磁盘上,其他数据都存储在内存中.说白点哪天你数据库死机 ...