持续原创输出,点击上方蓝字关注我吧


作者:不才陈某

博客:https://chenjiabing666.github.io

前言

  • 本篇文章是Myabtis源码分析的第三篇,前两篇分别介绍了Mybatis的重要组件和围绕着Mybatis中的重要组件教大家如何阅读源码的一些方法,有了前面两篇文章的基础,来看这篇文章的才不会觉得吃力,如果没有看过的朋友,陈某建议去看看,两篇文章分别是Mybatis源码解析之六剑客Mybatis源码如何阅读,教你一招!!!
  • 今天接上一篇,围绕Mybatis中的selectList()来看一看Mybatis底层到底做了什么,有什么高级的地方。

环境准备

  • 本篇文章讲的一切内容都是基于Mybatis3.5SpringBoot-2.3.3.RELEASE
  • 由于此篇文章是基于前两篇文章的基础之上,因此重复的内容不再详细赘述了。

撸起袖子就是干

  • 二话不说,先来一张流程图,Mybatis六剑客,如下:
  • 上图中的这六剑客在前面两篇文章中已经介绍的非常清楚了,此处略过。为什么源码解析的每一篇文章中都要放一张这个流程图呢?因为Mybatis底层就是围绕着这六剑客展开的,我们需要从全局掌握Mybatis的源码究竟如何执行的。

测试环境搭建

  • 举个栗子:根据用户id查询用户信息,Mapper定义如下:
List<UserInfo> selectList(@Param("userIds") List<String> userIds);
  • 对应XML配置如下:
<mapper namespace="cn.cb.demo.dao.UserMapper">
<!--开启二级缓存-->
<cache/>
<select id="selectList" resultType="cn.cb.demo.domain.UserInfo">
select * from user_info where status=1
and user_id in
<foreach collection="userIds" item="item" open="(" separator="," close=")" >
#{item}
</foreach>
</select>
</mapper>
  • 单元测试如下:
    @Test
void contextLoads() {
List<UserInfo> userInfos = userMapper.selectList(Arrays.asList("192","198"));
System.out.println(userInfos);
}

DEBUG走起

  • 具体在哪里打上断点,上篇文章已经讲过了,不再赘述了。

  • 由于SpringBoot与Mybatis整合之后,自动注入的是SqlSessionTemplate,因此代码执行到org.mybatis.spring.SqlSessionTemplate#selectList(java.lang.String, java.lang.Object),如图1

  • 从源码可以看到,实际调用的还是DefaultSqlSession中的selectList方法。如下图2

  • 「具体的逻辑如下」

    1. 根据Mapper方法的全类名从Mybatis的配置中获取到这条SQL的详细信息,比如paramterType,resultMap等等。
    2. 既然开启了二级缓存,肯定先要判断这条SQL是否缓存过,因此实际调用的是CachingExecutor这个缓存执行器。
  • DefaultSqlSession只是简单的获取SQL的详细配置,最终还是把任务交给了Executor(当然这里走的是二级缓存,因此交给了缓存执行器)。下面DEBUG走到CachingExecutor#query(MappedStatement, java.lang.Object, RowBounds,ResultHandler),源码如下图3

  • 上图中的query方法实际做了两件事,实际执行的查询还是其中重载的方法List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql),如下图4

  • 根据上图源码的分析,其实CachingExecutor执行的逻辑并不是很难,反倒很容易理解,「具体的逻辑如下」

    1. 如果开启了二级缓存,先根据cacheKey从二级缓存中查询,如果查询到了直接返回
    2. 如果未开启二级缓存,再执行BaseExecutor中的query方法从一级缓存中查询。
    3. 如果二级缓存中未查询到数据,再执行BaseExecutor中的query方法从一级缓存中查询。
    4. 将查询到的结果存入到二级缓存中。
  • BaseExecutor中的query方法无非就是从一级缓存中取数据,没查到再从数据库中取数据,一级缓存实际就是一个Map结构,这里不再细说,真正执行SQL从数据库中取数据的是SimpleExecutor中的public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)方法,源码如下图5

  • 从上面的源码也是可以知道,在真正执行SQL之前,是要调用prepareStatement(handler, ms.getStatementLog())方法做一些参数的预处理的,其中涉及到了六大剑客的另外两位,分别是ParameterHandlerTypeHandler,源码如图6

  • 从上图可以知道设置SQL参数的真正方法是handler.parameterize(stmt),真正执行的是DefaultParameterHandler中的setParameters方法,由于篇幅较长,简单的说一下思路:

    1. 获取所有参数的映射
    2. 循环遍历,获取参数的值,使用对应的TypeHandler将其转换成相应类型的参数。
    3. 真正的设置参数的方法是TypeHandlersetParameter方法
  • 继续图6的逻辑,参数已经设置完了,此时就该执行SQL了,真正执行SQL的是PreparedStatementHandler中的<E> List<E> query(Statement statement, ResultHandler resultHandler)方法,源码如下图7

  • 上图的逻辑其实很简单,一个是JDBC执行SQL语句,一个是调用六剑客之一的ResultSetHandler对结果进行处理。

  • 真正对结果进行处理的是DefaultResultSetHandler中的handleResultSets方法,源码比较复杂,这里就不再展示了,具体的逻辑如下:

    1. 获取结果映射(resultMap),如果没有指定,使用内置的结果映射
    2. 遍历结果集,对SQL返回的每个结果通过结果集和TypeHandler进行结果映射。
    3. 返回结果
  • ResultSetHandler对结果处理结束之后就会返回。至此一条selectList()如何执行的大概心里已经有了把握,其他的更新,删除都是大同小异。

总结

  • Mybatis的源码算是几种常用框架中比较简单的,都是围绕六大组件进行的,只要搞懂了每个组件是什么角色,有什么作用,一切都会很简单。

  • 一条select语句简单执行的逻辑总结如下(前提:「默认配置」):

    1. 「SqlSesion」#SqlSessionTemplate.selectList()实际调用#DefaultSqlSession.selectList()
    2. 「Executor」#DefaultSqlSession.quer()实际调用的是#CachingExecutor().query(),如果二级缓存中存在直接返回,不存在调用#BaseExecutor.quer()查询一级缓存,如果一级缓存中存在直接返回。不存在调用#SimpleExecutor.doQuery()方法查询数据库。
    3. 「StatementHandler」#SimpleExecutor.doQuery()生成StatementHandler实例,执行#PreparedStatementHandler.parameterize()方法设置参数,实际调用的是#ParamterHandler.setParameters()方法,该方法内部调用TypeHandler.setParameter()方法进行类型转换;参数设置成功后,调用#PreparedStatementHandler.parameterize().query()方法执行SQL,返回结果
    4. 「ResultSetHandler」#DefaultResultSetHandler.handleResultSets()对返回的结果进行处理,内部调用#TypeHandler.getResult()对结果进行类型转换。全部映射完成,返回结果。
  • 以上就是六剑客在Select的执行流程,如果有错误之处欢迎指正,如果觉得陈某写得不错,有所收获,关注分享一波。

Mybatis如何执行Select语句,你真的知道吗?的更多相关文章

  1. (3)一起来看下使用mybatis框架的select语句的源码执行流程吧

    本文是作者原创,版权归作者所有.若要转载,请注明出处.本文以简单的select语句为例,只贴我觉得比较重要的源码,其他不重要非关键的就不贴了 主流程和insert语句差不多,这里主要讲不同的流程,前面 ...

  2. SQL 查询总是先执行SELECT语句吗?你们都错了!

    SELECT语句中子句的顺序.SELECT语句中使用时必须遵循的次序. 经过一段时间的学习,我们知道了SELECT语句超简版的语法如下: SELECT 字段名 FROM 表名 后来,我们又陆续学习了W ...

  3. MyBatis(二):Select语句传递参数的集中方案

    从别人说的方案中看出,传递参数方案还挺多,不如自己整理下,以便以后使用过程中有个笔记回忆录. 1.传递一个参数的用法: 配置文件 <select id="getById" r ...

  4. MyBatis直接执行sql语句mapper

    <select id="queryBySql" resultType="HashMap"> <![CDATA[ ${sql} ]]> & ...

  5. 在mybatis执行SQL语句之前进行拦击处理

    转载自:http://blog.csdn.net/hfmbook/article/details/41985853 比较适用于在分页时候进行拦截.对分页的SQL语句通过封装处理,处理成不同的分页sql ...

  6. 原来select语句在MySQL中是这样执行的!看完又涨见识了!这回我要碾压面试官!

    大家好,我是冰河~~ MySQL作为互联网行业使用最多的关系型数据库之一,与其免费.开源的特性是密不可分的.然而,很多小伙伴工作了很多年,只知道使用MySQL进行CRUD操作,这也导致很多小伙伴工作多 ...

  7. 10.1(java学习笔记)JDBC基本操作(连接,执行SQL语句,获取结果集)

    一.JDBC JDBC的全称是java database connection java数据库连接. 在java中需要对数据库进行一系列的操作,这时就需要使用JDBC. sun公司制定了关于数据库操作 ...

  8. 如何阻止SELECT * 语句

    我们每个人都知道是个不好的做法,但有时我们还是要这样做:我们执行SELECT * 语句.这个方法有很多弊端: 你从你的表里返回每个列,甚至后期加的列.想下如果你的查询里将来加上了VARCHAR(MAX ...

  9. 在HibernateTemplate里执行Sql语句

    如下所示只能执行非Select语句: public static void executeSQL(HibernateTemplate hibernateTemplate, String sql) { ...

随机推荐

  1. Vercel托管博客

    背景 本站已托管至Vercel,但作为一名前端小白,昨天帮朋友托管博客到Vercel的过程中到处碰壁,就想写一篇博客记录一下. 注册登录 点击Vercel官网{% btn 'https://verce ...

  2. 【python接口自动化】- 使用requests库发送http请求

    前言:什么是Requests ?Requests 是⽤Python语⾔编写,基于urllib,采⽤Apache2 Licensed开源协议的 HTTP 库.它⽐ urllib 更加⽅便,可以节约我们⼤ ...

  3. python2.7用socks和socket设置代理

    接下来是最近遇到的一个代理问题. 背景:一个基于python2.7的自动化测试项目 目的:因调试需求,需要通过代理连接其他公司的前端网站,来检验项目运行所在的问题. 问题:RUN>等了1分钟没有 ...

  4. 笔记:Linux下软件的安装、CentOS更新yum源、LAMP环境搭建、kali软件管理

    一.Linux下软件的安装 方式:yum源 / rpm /源码安装 1.yum:(帮助管理员解决依赖关系) yum是通过分析rpm的包头数据后,根据各种软件的相关性质做出属性相对应的解决方案,然后可以 ...

  5. PhpStorm安装及破解流程

    下载完以后,把破解的jar包放到bin目录下,更改两个.vmoptions文件 我是安装了一个破解的和汉化包 jet是破解包,resource是汉化包,然后还要修改本地主机地址 例: 0.0.0.0 ...

  6. 修改Ceph 监视器地址

    原文链接:https://blog.csdn.net/lzw06061139/article/details/51953900 获取monmap转储当前Ceph集群的monmap到/tmp/monma ...

  7. Mac本软件安装之后打不开的解决办法

    Mac本软件安装之后打不开的解决办法 MacOS系统版本10.15.3 一,安装Axure RP 9 问题:安装之后打不开 提示“Axure RP 9已损坏,无法打开”或“打不开 Axure RP 9 ...

  8. Java 实例 - 查看当前工作目录

    package guyu.day0820; /** * @Author: Fred * @Date: 2020/8/20 14:25 */ public class Demo03 { public s ...

  9. 高并发&性能优化(一)------总体介绍

    [开篇词] 本文主要通过一些经典的高并发场景,以及一些基本的运维工具来讲述一些关于高并发以及性能优化相关的内容,主要包括性能瓶颈的定位,性能调优的思路和技巧等. [性能的衡量指标] ?什么是性能 性能 ...

  10. 从零开始的SpringBoot项目 ( 二 ) 使用IDEA创建一个SpringBoot项目

    工欲善其事 , 必先利其器 . IntelliJ IDEA 2019.3.3 x64的安装与破解 下面详细说明下如何使用idea创建我们的第一个springboot项目: 首先打开idea主界面选择 ...