mybatis3源码篇(2)——执行流程

news2024/7/4 5:47:41

mybatis 版本:v3.3.0

文章目录

  • 执行流程
    • MapperProxyFactory
      • MapperProxy
      • MapperMethod
        • execute
        • convertArgsToSqlCommandParam
        • ResultHandler
    • SqlSession
    • Executor(执行器)
      • StatementHandler(声明处理器)
      • ParameterHandler(参数处理器)
      • ResultSetHandler(结果集处理器)
        • getRowValue
        • storeObject
    • 动态sql
    • 缓存
    • 插件
    • 嵌套查询
    • resultSets

执行流程

MapperProxyFactory

众所周知,mapper在运行时会被代理,所以先看看mapper接口是如何被代理的。
SqlSession.getMapper -> Configuration.getMapper -> MapperRegistry.getMapper
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这里有个细节,MapperRegistry会先从自身的knownMappers缓存中去拿,如果没有才创建代理类。所以只要使用的是同一个MapperRegistry对象,那么Mapper接口将会共用Mapper代理类对象。

继续往下跟进,发现MapperProxyFactory中使用jdk代理创建了一个MapperProxy代理对象在这里插入图片描述

MapperProxy

接着来看 MapperProxy是的代理逻辑——invoke方法。
在这里插入图片描述
从代理逻辑中可以看出,不会代理Object中的方法。并且实际执行逻辑给到了MapperMethod,同时获取MapperMethod也用到了缓存的设计。
在这里插入图片描述

MapperMethod

MapperMethod在变量上只有两个成员变量。可以粗俗这么理解:MapperMethod = sql语句 + 方法描述
在这里插入图片描述

execute

现在来看看,他是怎么执行的。
在这里插入图片描述
在MapperMethod中,虽然对增删改查都做了各自的操作,但最终都通过SqlSession去操作数据库。
INSERT -> sqlSession.insert
UPDATE -> sqlSession.update
DELETE -> sqlSession.delete
比较复杂一点的就是SELECT
如果方法没有返回值且有自定义resultHandler,则执行executeWithResultHandler -> sqlSession.select
如果方法返回多条记录,则执行executeForMany -> sqlSession.selectList
如果方法返回一个Map,则执行executeForMap -> sqlSession.selectMap
否则 -> sqlSession.selectOne

convertArgsToSqlCommandParam

在这里插入图片描述
根据方法传递参数,构建sql参数。除没有参数返回null和只有一个参数返回其本身外,其余情况都是返回一个map。这里同时也解释了sql中支持#{param1},#{param2},#{param3}…写法的原因。

ResultHandler

回到刚刚的execute代码中关于executeWithResultHandler的判断。
在这里插入图片描述
反正我看到这里是觉得挺奇怪的,为什么自定义resultHandler不能有返回值?
我个人认为(欢迎评论区探讨),关键在于resultHandler的设计。
在这里插入图片描述
实际上,resultHandler接口中只有一个方法就是handlerResult,并且这个方法还没有返回值。
意思就是说,我现在已经把查询结果给到handler了,剩下的事情我就不管了,结果要怎么处理那是handler自己的事情。就算我后面想要获取handler处理后的结果也得看handler愿不愿意给我(有没有给我提供方法)。
而mybatis为了提高开发效率,只是对返回值是list和map做了扩展,额外提供了sqlSession.selectList,sqlSession.selectMap用于这两种场景(sqlSession.selectOne实际上只是selectList后返回第一个元素)使得mapper可以返回这两种类型。
目前mybatis提供的resultHandler实现有两种:DefaultResultHandler和DefaultMapResultHandler,分别用于处理返回类型是list和map的映射。
在这里插入图片描述
对于返回值是list的情况,可以说是天然支持的,因为从数据库中查询出来的结果就是一个list,所以Executor默认就会使用DefaultResultHandler返回list。
在这里插入图片描述
在这里插入图片描述
至于Map的情况,则是SqlSession做的扩展
在这里插入图片描述

SqlSession

刚提到代理类最终会通过sqlSession去操作数据库,而sqlSession实际上又是通过Executor去操作数据库,以select为例。
在这里插入图片描述

Executor(执行器)

Executor的继承关系如下:
在这里插入图片描述
对于query查询方法来说,最终都会调到BaseExecutor这个基类。

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    //如果已经关闭,报错
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    //先清局部缓存,再查询.但仅查询堆栈为0,才清。为了处理递归调用
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      //加一,这样递归调用到上面的时候就不会再清局部缓存了
      queryStack++;
      //先根据cachekey从localCache去查
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        //若查到localCache缓存,处理localOutputParameterCache
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        //从数据库查
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      //清空堆栈
      queryStack--;
    }
    if (queryStack == 0) {
      //延迟加载队列中所有元素
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      //清空延迟加载队列
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
    	//如果是STATEMENT,清本地缓存
        clearLocalCache();
      }
    }
    return list;
  }

BaseExecutor中实现了对一级缓存的支持,对应里面的localCache变量。因为每一个sqlSession都会新建一个executor,所以一级缓存是sqlSession级别的缓存。
为减少篇幅,直接来到最常用的SimpleExecutor.doQuery(queryFromDatabase方法进)。
在这里插入图片描述
每一次查询都会根据configuration中的配置,新建一个StatementHandler。
在这里插入图片描述

在新建的同时也安装了插件

StatementHandler(声明处理器)

从上面可以得知,默认使用的是RoutingStatementHandler。但实际上他不是真正干事的家伙,他更像是一个路由/一个代理。根据statementType的不同创建不同的委托。
在这里插入图片描述
StatementHandler的继承关系如下
在这里插入图片描述
其中最常用的是PreparedStatementHandler
除此之外,从BaseStatementHandler的构造方法中可以看出,**新建StatementHandler的同时也会新建ParameterHandler和ResultSetHandler并赋值给StatementHandler。**所以执行器只需面向一个StatementHandler即可。
在这里插入图片描述

ParameterHandler(参数处理器)

接下来看看执行器是如何对参数进行处理的。跟踪doQuery里面的prepareStatement方法得到。
在这里插入图片描述
parameterHandler.setParameters
在这里插入图片描述
TypeHandler针对参数类型的处理就是在这一步实现的。

ResultSetHandler(结果集处理器)

结果集是如何查询和处理的?
在这里插入图片描述
resultSetHandler.handleResultSets
在这里插入图片描述
每一个结果集处理逻辑
在这里插入图片描述
每一行数据的处理逻辑
在这里插入图片描述
在这里插入图片描述

getRowValue

根据查询结构得到一个POJO对象
在这里插入图片描述
创建POJO对象代码:
在这里插入图片描述
这里有几个细节:
1、创建实例时会再次使用到TypeHandler(所以TypeHandler不仅在处理参数时有用到)
2、如果需要会使用objectFactory创建实例(默认是DefaultObjectFactory,这个可自定义配置)
3、会尝试自动映射,把值按字段名填充上去。且在resultMap中明确指定的列,会被加入mappedColumnNames,不会参与自动映射。即自动映射只会映射unmappedColumnNames中的列

storeObject

把生成的POJO结果交给resultHandler
在这里插入图片描述
至此就完成了一个最基本的执行流程。

虽然里面还有很多细节没有提到,不过代码上大体就是这么一个流程。

动态sql

动态sql的每一个标签mybatis都将其描述成一个sqlNode,并且不同标签有不同的实现类。比如ChooseSqlNode对应choose标签,IfSqlNode对应if标签,WhereSqlNode对应where标签…至于在处理标签过程中有关表达式的判断则用的是ognl表达式,最后把所有标签的结果拼接起来得到最终的sql。
在这里插入图片描述

缓存

1、一级缓存是默认打开的,在BaseExecutor中实现了对一级缓存的支持,对应的是localCache变量。因为每一个sqlSession都会新建一个executor,所以一级缓存是sqlSession级别的缓存。同时当同一个sqlSession有对数据进行更新操作的话,会清空缓存导致缓存失效。
2、二次缓存默认是不打开的,属于nameSpace级别的缓存。如果要打开二级缓存,需要在mybatis.xml中添加cache标签或者设置cacheEnabled为true,并且具体实体类还要支持序列化。二级缓存是在sqlSession关闭的时候才存入的。
3、缓存器有多个实现,并且这些缓存器是可以同时使用的,设计模式是责任链模式
在这里插入图片描述

插件

只有四大组件可以安装插件,会对具体拦截的方法进行代理。具体安装的地方在Configuration生成各自实例的地方。
Executor (update、query、commit、rollback等方法);
在这里插入图片描述

StatementHandler (prepare、parameterize、batch、updates query等方法);
在这里插入图片描述

ParameterHandler (getParameterObject、setParameters方法);
在这里插入图片描述

ResultSetHandler (handleResultSets、handleOutputParameters等方法);
在这里插入图片描述

嵌套查询

考虑这么一种场景,其中sub这个属性是怎么被赋值的?
在这里插入图片描述
mybatis将其描述成了一个嵌套查询。即在处理结果集,填充属性的时候,如果发现这是一个嵌套查询,则就会再次调用执行器,去执行对应的sql语句并处理结果集赋值给该属性。

这里有个细节,就是假如主查询返回10条数据,则相对应的就会有10个嵌套查询。

相关代码体现在 DefaultResultSetHandler.getRowValue获取POJO对象中的填充属性值方法。
在这里插入图片描述

resultSets

再考虑这么一个场景
在这里插入图片描述
注意看,这里的查询语句有两个select,即说明执行这个mappedStatement会有两个结果集。这里分别命名sql1,sql2。而在resultMap中sub属性对应sql2结果集,说明sql2结果集关联sql1结果集的sub属性。
最终实现的效果是,数据库有且仅会查询两次,得到两个结果集并依次处理。最终返回的结果中,几乎等效于嵌套查询的外键关联。唯一不同的就是,若sql2结果集中的任一条数据在sql1结果集中找不到对应的关联数据会报错。
这是因为mybatis如果用的是resultSets,那么这两个结果集中并没有什么主从关系。mybatis是依次处理结果集的,如果在处理的过程中发现该结果集有关联另一个结果集,就会其结果和相关联的结果集连接起来。

这里有个细节,resultSets的连接是基于内存的,毕竟总共也就只执行了两个sql,不可能依靠数据库连接。

相关代码体现在DefaultResultSetHandler
在这里插入图片描述

这个resultSets用的比较少,而且个人认为功能也比较鸡肋。也没有太仔细的去深究,就这样吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/458515.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【设计模式】我对设计模式的C语言解读(下)

书接上回 由于内容太多&#xff0c;编辑器太卡了&#xff0c;所以分P了 上P在这里 目录 书接上回备忘录模式观察者模式 备忘录模式 备忘录模式的介绍: https://refactoringguru.cn/design-patterns/memento 备忘录模式的C实现: https://refactoringguru.cn/design-patterns/m…

【数据挖掘与商务智能决策】第十三章 数据降维之PCA 主成分分析

13.1.2 PCA主成分分析代码实现 1.二维空间降维Python代码实现 import numpy as np X np.array([[1, 1], [2, 2], [3, 3]]) Xarray([[1, 1],[2, 2],[3, 3]])# 也可以通过pandas库来构造数据&#xff0c;效果一样 import pandas as pd X pd.DataFrame([[1, 1], [2, 2], [3, 3…

二分查找【数组】

⭐前言⭐ ※※※大家好&#xff01;我是同学〖森〗&#xff0c;一名计算机爱好者&#xff0c;今天让我们进入复习模式。若有错误&#xff0c;请多多指教。更多有趣的代码请移步Gitee &#x1f44d; 点赞 ⭐ 收藏 &#x1f4dd;留言 都是我创作的最大的动力&#xff01; 题目 70…

接口测试用例设计思路

&#xff08;我的公众号“墨石测试攻略”&#xff0c;关注获取软件测试相关知识及整套接口测试实战项目&#xff01;&#xff09; 接口测试用例的设计&#xff0c;从功能测试角度来说&#xff1a;首先需要分析接口文档。 现在很多公司都使用swagger来管理接口。swagger中可以…

fMRI时间序列振幅和相位对功能连接分析的影响

导读 目的&#xff1a;fMRI领域的一些研究使用瞬时相位(IP)表征(源自BOLD时间序列的解析表征)考察了脑区之间的同步性。本研究假设来自不同脑区的瞬时振幅(IA)表征可以为脑功能网络提供额外的信息。为此&#xff0c;本研究探索了静息态BOLD fMRI信号的这种表征&#xff0c;用于…

SpringBoot AnnotationFormatterFactory接口+自定义注解实现类型转换

参考资料 自定义AnnotationFormatterFactory实现注解方式类型转换Spring MVC 基于AnnotationFormatterFactory接口实现自定义的规则 目录 一. 前期准备1.1. 自定义转换标记注解1.2 入参form 二. 实现AnnotationFormatterFactory接口&#xff0c;构建格式化Factory2.1 code补全…

【7】一篇文章学习 Linux 中一些硬核的常用知识

目录 一、systemctl二、软链接三、日期&#xff08;date 命令&#xff09;四、Linux 的时区(1) 修改时区(2) ntp 五、IP 地址六、主机名七、域名解析八、配置 Linux 的固定 IP 地址(1) 在 VMwareWorkstation 中配置 IP 地址网关和网段&#xff08;IP 地址的范围&#xff09;(2)…

[陇剑杯 2021]之Misc篇(NSSCTF)刷题记录④

NSSCTF-Misc篇-[陇剑杯 2021] jwt&#xff1a;[陇剑杯 2021]jwt&#xff08;问1&#xff09;[陇剑杯 2021]jwt&#xff08;问2&#xff09;[陇剑杯 2021]jwt&#xff08;问3&#xff09;[陇剑杯 2021]jwt&#xff08;问4&#xff09;[陇剑杯 2021]jwt&#xff08;问5&#xff0…

洗地性价比高的是哪款?性价比高的洗地机推荐

在如今人工智能随处可见的时代&#xff0c;洗地机已经成为了我们家庭清洁的得力助手&#xff0c;它用高效便捷的清洁方式&#xff0c;对于地面的灰尘或者地板之间的缝隙里的细小垃圾&#xff0c;能够快速清理&#xff0c;省时省力。然而&#xff0c;对于很多消费者来说&#xf…

一文带你学会如何写一份糟糕透顶的简历

我们每个人几乎都会面对找工作这件事&#xff0c;而找工作或者说求职首先就是要写一份简历。今天狗哥将以一个不同的视角带你写一份无与伦比&#xff0c;糟糕透顶的求职简历&#xff0c;说实话&#xff0c;其实几年前&#xff0c;我就是这么写的。 目录 1. 文件名 2. 基本信…

AttributeError: ‘ChatGLMModel‘ object has no attribute ‘prefix_encoder‘

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

基于蛋白-配体复合物药效团药物设计(Pharmacophore)

基于蛋白-配体复合物药效团药物设计&#xff08;Pharmacophore&#xff09; step 1.蛋白-配体复合物准备 点击File-->Import Structures导入之前已经下载好的1IEP.pdb&#xff08;Abl蛋白和Imatinib的晶体复合物&#xff09; 蛋白准备&#xff1a;点击Tasks--->Protei…

18、越狱

一、越狱 1.1 越狱 通过iOS系统安全启动链漏洞,从而禁止掉信任链中负责验证的组件.拿到iOS系统最大权限Root权限 1.2 iOS系统安全启动链 当启动一台iOS设备时,系统首先会从只读的ROM中读取初始化指令,也就是系统的引导程序(事实上所有的操作系统启动时都要经过这一步,只是过程…

Pytorch深度学习笔记(十一)卷积神经网络CNN

目录 1.概述 2.单通道卷积 3.多通道卷积 4.卷积层常见的参数 5.代码实现&#xff08;卷积神经网络训练MNIST数据集&#xff09; 推荐课程&#xff1a;10.卷积神经网络&#xff08;基础篇&#xff09;_哔哩哔哩_bilibili 1.概述 全连接神经网络&#xff1a;完全由线性层串…

最佳实践|如何写出简单高效的 Flink SQL?

摘要&#xff1a;本文整理自阿里巴巴高级技术专家、Apache Flink PMC 贺小令&#xff0c;在 Flink Forward Asia 2022 生产实践专场的分享。本篇内容主要分为三个部分&#xff1a; 1. Flink SQL Insight 2. Best Practices 3. Future Works Tips&#xff1a;点击「阅读原文」查…

android之 Launcher改造仿桌面排版的效果

一&#xff0c;背景 1.1 新接手一个灯光控制项目&#xff0c;其页面和效果还是比交复杂的&#xff0c;其中一个功能就是仿苹果桌面来排版灯具&#xff0c;支持拖拽&#xff0c;分组&#xff0c;分页。 拖动图标的时候判断是否空白位置还是已经有占位了&#xff0c;有的话就把…

pikachu靶场-RCE

RCE漏洞概述 可以让攻击者直接向后台服务器远程注入操作系统命令或者代码&#xff0c;从而控制后台系统。 远程系统命令执行 命令执行漏洞&#xff08;Command Execution&#xff09;即黑客可以直接在Web应用中执行系统命令&#xff0c;从而获取敏感信息或者拿下shell权限 更…

Linux离线状态下安装cuda、cudnn、cudatoolkit

目录 1. 下载与安装说明2. CUDA安装3. cuDNN安装4. cudatoolkit安装5. 测试安装成功 1. 下载与安装说明 工具包下载地址 CUDA历史版本下载地址&#xff1a;https://developer.nvidia.com/cuda-toolkit-archivecuDNN历史版本下载地址&#xff1a;https://developer.nvidia.com/r…

logback日志框架集成方式

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、logback是什么&#xff1f;二、使用步骤1.使用方式控制台输出配置文件输出配置html输出配置定期删除配置方式 总结 前言 提示&#xff1a;这里可以添加本文…

C++每日一练:最长递增区间 阿波罗的魔力宝石 投篮

文章目录 前言一、最长递增区间二、阿波罗的魔力宝石三、投篮总结 前言 今天的题太简单&#xff0c;甚至 “最长递增区间” 和 “投篮” 就是一个问题。实在没事干&#xff0c;也给做了&#xff01;直接上代码算了… 提示&#xff1a;以下是本篇文章正文内容 一、最长递增区间…