MyBatis3源码解析(5)查询结果处理

Mybatis 返回值配置理解 - 返回值是浮点数 BigDecimal 或整数 Integer的配置 - 返回指定实体类格式的 List 数组 的值,否则就会报如上错误的,其实仔细看看因为报错原因的意思就好了。mybatis中的所有查询标签,都必须返回。 阅读详情

简介

上篇中解析了MyBatis3中参数是如何传递处理的,本篇接着看看在获取到查询结果后,MyBatis3是如何将SQL查询结果与我们接口函数定义的返回结果对应的

源码

获取结果后处理的入口

在:MyBatis3源码解析(3)查询语句执行中,我们大致知道了查询结果处理的想代码位置,如下:

public class PreparedStatementHandler extends BaseStatementHandler {
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    // 前面的语句已经执行,处理结果返回
    return resultSetHandler.handleResultSets(ps);
  }
}

SQL结果处理

下面是具体的处理逻辑,先获取结果集和Mapper中定义的返回结果的相关信息,然后对应进行处理

public class DefaultResultSetHandler implements ResultSetHandler {
  @Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;
    // 获取结果集,并用自定义包装起来
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    // 获取ResultMap对象,一般只有一个,其实就是需要返回的类信息之类的
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      // 在这里会循环获取SQL结果集并进行转换
      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);
  }

  private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
    ResultSet rs = stmt.getResultSet();
    while (rs == null) {
      // move forward to get the first resultset in case the driver
      // doesn't return the resultset as the first result (HSQLDB 2.1)
      if (stmt.getMoreResults()) {
        rs = stmt.getResultSet();
      } else {
        if (stmt.getUpdateCount() == -1) {
          // no more results. Must be no resultset
          break;
        }
      }
    }
    return rs != null ? new ResultSetWrapper(rs, configuration) : null;
  }
}

我们一直跟下去,中间步骤不是太关键,下发是SQL Result遍历和结果转换的相关代码

public class DefaultResultSetHandler implements ResultSetHandler {
    private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
    ResultSet resultSet = rsw.getResultSet();
    skipRows(resultSet, rowBounds);
    // SQLresult遍历
    while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
      // 将单条SQL结果转成对象
      Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
      // 存储
      storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    }
   }

   private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    // 根据返回的类型生成对象
    Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
    if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
      final MetaObject metaObject = configuration.newMetaObject(rowValue);
      boolean foundValues = this.useConstructorMappings;
      if (shouldApplyAutomaticMappings(resultMap, false)) {
        foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
      }
      // 结果转换
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
      foundValues = lazyLoader.size() > 0 || foundValues;
      rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
    }
    return rowValue;
  }

  
  private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
      throws SQLException {
    final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
    boolean foundValues = false;
    // 结果相关的属性
    final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
    // 遍历,结果对应对应的值
    for (ResultMapping propertyMapping : propertyMappings) {
      // 列名称
      String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      if (propertyMapping.getNestedResultMapId() != null) {
        // the user added a column attribute to a nested result map, ignore it
        column = null;
      }
      if (propertyMapping.isCompositeResult()
          || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
          || propertyMapping.getResultSet() != null) {
        // 转换,获取值
        Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
        // issue #541 make property optional
        final String property = propertyMapping.getProperty();
        if (property == null) {
          continue;
        } else if (value == DEFERRED) {
          foundValues = true;
          continue;
        }
        if (value != null) {
          foundValues = true;
        }
        if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
	  // 更新结果
          // gcode issue #377, call setter on nulls (value is not 'found')
          metaObject.setValue(property, value);
        }
      }
    }
    return foundValues;
  }

  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 DEFERRED;
    } else {
      // 将SQL值转成对应java对象值,MyBatis3已内容Int Str等转换器
      final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
      final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      return typeHandler.getResult(rs, column);
    }
  }
}

在上面代码中,我们看到了:

  • 循环遍历SQL结果:ResultSet
  • 遍历SQL列,转成Java类型

MyBatis中已经内置很多的类型转换器,也可以自己自定义,比如内容Long如下:

public class LongTypeHandler extends BaseTypeHandler<Long> {
  @Override
  public Long getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    long result = rs.getLong(columnName);
    return result == 0 && rs.wasNull() ? null : result;
  }
}

总结

本篇文章中探索的MyBatis中如何将查询结果转换成我们想要的Java对象

  • SQL执行
  • 遍历ResultSet
  • 遍历数据库Column,根据列名称,获取对应的值,转成Java对象
Mybatis源码详解 基于3.5.10的版本对Mybatis源码从加载到执行全流程的解析,通过与传统JDBC方法的对比更深一步的了解Mybatis帮我们做了什么,以及是如何做的 阅读详情

相关推荐

【12.MyBatis源码剖析与架构实战】 5.4 查询操作

MyBatis执行SQL查询的核心流程是通过StatementHandler组件与JDBC交互的。文章通过序列图展示了从Executor到StatementHandler的完整调用链,重点分析了PreparedStatementHandler处理预编译SQL的过程。源码层面详细解读了三个关键步骤:1)根据MappedStatement创建特定类型的StatementHandler;2)准备Statement对象并设置参数;3)执行查询并处理结果集。其中,parameterize()方法通过Parameter

qq_39275653的博客 1092

mybatis 返回Integer、Double、String等类型的数据

在使用mybatis的过程中会遇到只返回单独数据类型的问题会用到resultType。 //返回Integer select id="getSpeedByLinkId" parameterType="java.lang.String" resultType="Integer"> SELECT speed from dws_tfc_state_speed_link_last_r

fenfenguai的专栏 8万+

深入解析MyBatis源码与实战

摘要:本文系统解析MyBatis核心架构与实现原理,涵盖配置解析、SQL执行、结果映射三大模块。通过动态SQL生成、缓存机制(一级/二级缓存)、插件开发(Interceptor接口)等典型示例,深入剖析源码实现。结合SpringBoot集成场景,展示CRUD操作、动态SQL拼接、事务管理、多数据源配置等实战方案,并给出批量操作优化(BatchExecutor)、类型处理器扩展等高级特性实现。特别说明流程图关键节点:从SqlSessionFactory构建到Executor执行,通过StatementHand

keny-大成的博客 1358

Mybatis映射BigDecimal时出现科学计数法

Mybatis映射BigDecimal时出现科学计数法,0变成如0E-9

gaoshan12345678910的博客 3246

5分钟搞定单细胞marker基因气泡图:scDotPlot保姆级教程(含Seurat适配)

本文提供了一份详细的scDotPlot R包使用教程,帮助用户在5分钟内完成单细胞marker基因气泡图的绘制。教程涵盖环境准备、Seurat和SingleCellExperiment对象的适配处理、进阶可视化技巧及自动化报告生成,特别适合生物信息学新手和临床研究者快速掌握这一高效的可视化工具。

weixin_29047545的博客 261

别再搞混了!用示波器实测SVPWM:相电压、线电压、端电压波形到底长啥样?(附TI/ST官方笔记解读)

本文详细解析了SVPWM波形实测中的相电压、线电压和端电压的区别与测量技巧,结合TI/ST官方笔记,揭示了马鞍形波形的形成原理及常见误区。通过示波器实测案例,帮助电力电子工程师准确理解并应用这些关键概念,提升电机控制系统的调试效率。

weixin_30632883的博客 431

MyBatis映射实体时将BigDecimal值映射为科学计数法

个人记录 MyBatis映射实体时将BigDecimal值映射为科学计数法 一、错误显示 实体: 返回:即数量为:1e-8 二、解决办法 1. 在实体或返回实体中加一个String类型的字段 2. 在sql查询的时候将原来 BigDecimal 的值使用 concat() 函数转换,如下图 ...

慌途L 1万+

深入解析CAN总线BusOff故障的检测与恢复机制

本文深入解析了CAN总线BusOff故障的检测与恢复机制。BusOff是CAN节点因发送错误过多而被强制离线的一种严重状态。文章详细阐述了其触发原理,即发送错误计数器(TEC)超过255,并重点介绍了通过软件实现的快慢恢复机制,以增强网络在异常情况下的稳定性和韧性。

weixin_29158047的博客 523

MyBatis查询结果处理

1 处理核心流程 PreparedStatement的查询结果需要进行映射 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; // 装换preparedStatement ps.execute(); // 执行SQL ret

Alexon Xu的博客 1462

告别CAPL编程!用Matlab/Simulink+CANoe实现复杂ECU仿真的5个技巧

本文介绍了如何利用Matlab/Simulink与CANoe进行联合仿真,以替代复杂的CAPL编程,高效实现复杂ECU仿真。文章分享了5个核心技巧,涵盖信号映射、模型编译集成、实时参数调整、性能优化及数据分析,帮助算法工程师构建稳定强大的仿真环境,显著提升开发与测试效率。

rgv234567的博客 296

伺服系统控制原理深度解析

摘要:本文系统阐述了伺服系统的控制原理与应用。首先介绍了伺服系统的定义、组成及其在工业自动化中的重要性;然后详细分析了反馈控制机制、稳定性判据和性能指标;重点推导了传递函数和状态空间两种数学模型,并通过LaTeX公式展示了二阶系统建模过程;进而对比了PID控制、状态反馈及自适应控制等方法的优缺点;最后结合数控机床等案例,验证了理论方法的有效性,并展望了伺服系统智能化发展趋势。全文兼顾理论深度与实践指导性,为控制工程领域提供了系统的技术参考。

技术工作者 1263

【MyBatis系列6】想要学会MyBatis,看完这一篇就够了

深入剖析SqlSession前言SqlSession总结 前言 作为MyBatis四大对象之一的SqlSession,可以说是最复杂的对象了,SqlSession就是用来真正执行sql语句的对象,执行一条sql语句,两个关键点就是JavaBean参数要和数据库字段映射,返回结果也要实现数据库字段和JavaBean的映射,那么SqlSession是如何做到这一切的呢? 请往下看。。。 本文将带您揭开SqlSession的神秘面纱。 SqlSession 总结 本文主要分析了MyBatis中的四大核心对象:Sq

zwx900102的博客 7233

ERROR: Could not install packages due to an OSError: [Errno 2] No such file or directory解决方案

本文主要介绍了ERROR: Could not install packages due to an OSError: [Errno 2] No such file or directory解决方案,希望能对使用Python的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 11万+

mybatis源码解析(mybatis plus)

mybatis plus源码解析

何忆清风 2043

【UVM实战】第一章:配置UVM环境(以VCS为例),跑通书上的例子(包括清理中间文件方法、使用DVE查看波形方法、命令执行原理探究)

如何配置UVM的VCS运行环境,运行书本上的例子呢?本节将会为你详细解答!

悟已往之不谏 知来者之可追 8131

26.Labview波形图、XY图、强度图使用精讲

1. 波形图基础概念,如何使用,1.1 波形图与波形图表1.2 XY图如何设计、如何使用?1.3 强度图原理、如何使用、使用案例;2. 图形的参数设置2.1 波形图及波形图表的外观设计2.2 XY图的前面板设计2.3 强度图属性设置。

关注我,更多技术问题欢迎私聊~ 7497

Mybatis3 源码解析系列

Mybatis作为一个优秀的Java持久化框架,在我们的日常工作中相信都会用到,本次源码解析系列,就开始探索下Mybatis

github_35735591的博客 1076

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

MyBatis源码阅读(七) --- 查询结果集封装流程

一、概述 前面一篇文章我们分析了mapper方法具体的执行流程,跟踪代码到了resultSetHandler.handleResultSets()处理结果集这里,这也是Mybatis处理数据的最后一个步骤了。 试想一下,如果让我们自己组装结果,我们该如何实现呢,可以大概想一下。 如果是我们自己实现的话,有几个关键的步骤: 1、准备一个List集合存放结果集; 2、肯定要拿到我们在Mapper.xml中配置的resultType属性,拿到之后,通过反射,我们应该可以拿到对应的Class类; 3、拿

weixiaohuai的博客 1068

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

【源码系列】MyBatis原理源码

Mybatis的核心是Jdk的动态代理和各个组件,整个运行可以分为两大步骤: 1. 准备阶段 1. 解析配置文件获取全局Configuration对象,该过程中为每个Mapper接口创建MapperProxyFactory对象, 2. 有了Configuration对象以后通过构建者模式构建SqlSessionFactory 3. 通过SqlSessionFactory创建SqlSession对象,SqlSession包含了Executor(Sql的执行器),如果有插件,Executor会织入插件逻辑(通

苏雨丶 6313

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

MyBatis 3结果集处理终极指南:高效处理百万级查询结果

MyBatis 3作为Java生态中流行的SQL映射框架,其结果集处理能力直接影响应用性能。本文将系统讲解如何通过ResultHandler、RowBounds和Cursor等核心组件,轻松应对百万级数据查询场景,让你的数据处理效率提升300%! ## 一、ResultHandler:自定义结果集处理的利器 ResultHandler是MyBatis结果集处理的核心接口,位于`org.apac

gitblog_00526的博客 650

Spring源码解析(十):spring整合mybatis源码

通过sqlSession获取对应的代理对象代理对象执行sql完成数据库操作// 1.解析XML配置// 2.基于解析好的XML配置创建一个SqlSessionFactory// 3.通过SqlSessionFactory,创建一个SqlSession// 4.获取一个代理对象// 5.调用代理对象的方法System.out.println("代理对象查询结果:" + mapper.selectOne(1));根据@MapperScan注解扫描指定路径下接口。

从基础到源码解析的学习记录 1273

MyBatis核心源码解析 从SqlSession到Mapper接口的绑定过程

摘要:本文深度解析MyBatis框架的核心原理与最佳实践。从架构总览、SqlSession创建机制到Mapper动态代理实现,详细剖析了SQL执行链路、缓存机制和插件原理。通过源码分析揭示MyBatis内部工作原理,包括Configuration加载、Executor调度和StatementHandler执行过程。针对常见性能问题提供优化方案,如批量操作、分页查询和缓存使用技巧。最后总结企业级开发中的"MyBatis军规",包括SQL编写规范、缓存策略和监控方案,帮助开发者规避典型陷阱,

七夜zippoe的博客 2147

MyBatis 3.5 源码导读

MyBatis 是一个流行的持久层框架,提供了对 SQL 的良好支持和灵活的配置,特别是在 Java 应用程序中。MyBatis 3.5 的源码导读可以帮助你更好地理解其内部工作机制和设计理念。

大道至简 690

源码学习之MyBatis的底层查询原理

本文通过MyBatis一个低版本的bug(3.4.5之前的版本)入手,分析MyBatis的一次完整的查询流程,从配置文件的解析到一个查询的完整执行过程详细解读MyBatis的一次查询流程,通过本文可以详细了解MyBatis的一次查询过程。在平时的代码编写中,发现了MyBatis一个低版本的bug(3.4.5之前的版本),由于现在很多工程中的版本都是低于3.4.5的,因此在这里用一个简单的例子复现问题,并且从源码角度分析MyBatis一次查询的流程,让大家了解MyBatis的查询原理

京东科技开发者 1919

【Mybatis源码探索】 --- Mybatis查询过程核心源码解读 --- 先聊聊selectOne方法

文章目录1 源码入口2 sqlSessionFactory.openSession()源码分析2.1 openSession方法 --- 获取SqlSession对象的骨架2.2 Executor对象的创建 --- 真正与数据库打交道的其实是Executor2.2.1 Executor对象创建源码解析2.2.2 Executor介绍3 Mybatis查询过程核心源码解读3.1 selectO...

nrsc 3573

源码学习之 MyBatis 的底层查询原理

MyBatis 源代码的目录是比较清晰的,基本上每个相同功能的模块都在一起,但是如果直接去阅读源码的话,可能还是有一定的难度,没法理解它的运行过程,本次通过一个简单的查询流程从头到尾跟下来,可以看到 MyBatis 的设计以及处理流程,例如其中用到的设计模式:图 25 MyBatis 代码结构图组合模式:如 ChooseSqlNode,IfSqlNode 等。

Q54665642ljf的博客 466
上一篇: MyBatis3源码解析(4)参数解析
下一篇: MyBatis3源码解析(6)TypeHandler使用
_萧_
博客等级 码龄10年 55粉丝 157原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值