阿昌教你看懂mybatisplus的sql执行流程

前言

前置版本:MybatisPlus3.0.5

前些日子写了两篇关于mybatis相关的文章,一篇是《sqlSessionFactory的创建过程》,一篇是《sql语句的创建过程 》,看这篇,建议把上面两篇看了再来 (。・∀・)ノ゙

这一次打算记录一下sql语句的执行流程,同样还是建议打开源码一起翻着对比看比较好


前戏

我们以如下代码开始入手:↓

public boolean isExist(VisitInfoTable item) {
    return baseMapper.selectCount(new QueryWrapper<VisitInfoTable>().
                                  eq("ip",item.getIp()).
                                  eq("time",item.getTime()))>0;
}
复制代码

首先,我们知道项目启动后,SpringBoot会装配对应的mapper接口文件和.xml文件到mybatis的configurationmappedStatements中。里面有对应sql的全部内容,包括完整语句,拆分拼接语句等的内容

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rffsmEte-1637916210359)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126115153276.png)]

在开始之前,mybatis的sql执行流程中必然会涉及到代理设计模式,具体的内容可以参考《代理模式


正文

我们调用selectCount()方法,查询数据库的时候。

他会执行这个方法对应的代理对象。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E13znZ7P-1637916210361)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126132136996.png)]

他所对应的代理对象执行的类PageMapperProxy

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yDQ7E3Mp-1637916210363)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126132313039.png)]

因为他实现了InvocationHandler,所以他需要重写了invoke()方法

  • 上来先判断他是否是Object类本身,如果是就不增强

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jBqBiRoo-1637916210364)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126133234447.png)]

  • 判断缓存中是否有对应方法的缓存,如果缓存中没有,则创建一个,并添加到缓存中

[外上传(imaming\Typora\typora-user-images\image-20211126133604760.png)]

  • 调用mapperMethod.execute方法执行sql

在这里插入图片描述


判断对应的sql执行类型

  • 首先上来会判断此次执行的sql是什么类型的操作

在这里插入图片描述

  • 这次我们执行的操作是Select操作,他细分了具体的内容,如是否有返回值/多个/map/标记等的具体划分

在这里插入图片描述

  • 这次的查询都不符合,我们查询的是count数量,所以最后返回的是一个值,就执行else里面的convertArgsToSqlCommandParam
  • 一看方法名都能猜的出来,就是把对应的sql和我们的传入的参数做整合在一起

在这里插入图片描述

command.getName()获取到需要执行方法的全限定名

在这里插入图片描述

param对应传入的参数

  • sqlSession.selectOne(command.getName(), param)这个方法又被代理

在这里插入图片描述

  • 执行的AOP前后额外内容的结构如下:↓

在这里插入图片描述


获取对应sqlSession会话

  • 他在前置执行获取了对应的sqlSession,在里面执行了对应的操作,并执行openSession(),打开获取这次会话

在这里插入图片描述

  • 注意,这里的autoCommit为false,不自动提交会话

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CM8IHuQp-1637916210374)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126141951653.png)]

  • 根据对应的设置构造一个DefaultSqlSession对象返回

在这里插入图片描述

  • 返回sqlSession

在这里插入图片描述


  • 发现method.invoke(sqlSession, args),它又又又被代理了 3次!!!

在这里插入图片描述


selectList

  • 最后,他执行selectList()

在这里插入图片描述

  • 我们会发现,上面我们是查询一个返回对象,但是最后还是执行的selectList()方法。
  • 但是,他会在下面对个数进行判断,如果是一个拿到集合第一个返回,如果多个就报TooManyResultsException错误,不然就返回null

在这里插入图片描述

  • 他封装了两层的selectList(),传入的statement就是方法的全限定名,parameter顾名思义就是这次查询的参数

在这里插入图片描述

  • 最最最最最最最最最!!!!关键的一步骤。

在这里插入图片描述

  • MappedStatement你还熟悉吗???configuration.getMappedStatement(statement)
  • 他根据方法的全限定名来拿到已经拼接好的sql语句

在这里插入图片描述

  • 拿到的这个ms,就是这个方法对应的sql语句
MappedStatement ms = configuration.getMappedStatement(statement);
复制代码
  • 然后用执行executor
executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
复制代码
  • 但是发现,它又被代理了一层

在这里插入图片描述


Plugin资料

  • 这个叫Plugin的类是啥,我们看名字可以看出是插件

  • 这个Plugin类的mybatis提供第三方进行拦截的插件类,加上插件就是通过这个类的wrap()方法

在这里插入图片描述

  • 比如PageHelper分页插件,他就会有个PageInterceptor

在这里插入图片描述


  • 这里就是自定义的拦截器第三方插件了,他就循环遍历。

在这里插入图片描述

  • 比如阿昌这里装了PageHelper,那这里就会执行PageHelper的拦截器PageInterceptorintercept方法

在这里插入图片描述

  • 因为这里,我们并没有使用PageHelper的分页方法,所以就不会执行,会被放行,他会根据对应参数个数,来生产对应的CacheKey

在这里插入图片描述

CacheKey:如下,根据各种信息拼接,最后成一个长字符串

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nrgwAhD0-1637916210386)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126145758389.png)]

  • 肯定有老哥说,搞了半天怎么还没执行我们写的sql方法啊!!!!!
  • 对于这里生成了对应的CacheKey后,他就会执行正式的方法了!!!。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-am8d7gRT-1637916210386)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126145721306.png)]

  • executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql):↓

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jpORYcg4-1637916210387)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126145918924.png)]

  • 他先看看MappedStatement的cache缓存里面有没有,那这里,因为我们是第一次执行,必然没有缓存

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tYKYLR4F-1637916210388)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126150043856.png)]

  • 查询的过程,一开始会判断Executor关了没。没关就去缓存中拿,有就处理下返回,没有就去从数据库查。
  • 这里会涉及到一个queryStack,queryStack为0时就会清空本地的缓存clearLocalCache()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v1zvTq5r-1637916210389)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126151937849.png)]


  • 那我们看如果没有缓存,从数据库中查是什么逻辑queryFromDatabase

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OqUdXvhj-1637916210389)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126152201942.png)]

  • 他会给本地缓存先放一份这个内容,

    • Key:为我们之前生成的CacheKey

    • Value:EXECUTION_PLACEHOLDER占位符

    • localCache.putObject(key, EXECUTION_PLACEHOLDER);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9JFJTBeE-1637916210390)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126152419092.png)]

  • 然后他就去数据库查,他就怎么查呢,这里他会根据对应那种类型的Executor来有不同的逻辑,我们这里是用的SimpleExecutor

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OxbwGkRM-1637916210391)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126152534797.png)]

  • 根据ms获取到对应的mybatis的配置类信息,然后配置信息参数,sql语句等信息获取到对应的StatementHandler,他就是实际负责操作 Statement 对象与数据库进行交流

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PX6356Or-1637916210392)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126152742539.png)]

有人肯定会不知道Statement 是什么,Statement 是Java 执行数据库操作的一个重要接口,用于在已经建立数据库连接的基础上,向数据库发送要执行的SQL语句。Statement对象,用于执行不带参数的简单SQL语句。

  • 现在有了Statement,和resultHandler结果处理器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dpSoMvsM-1637916210393)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126153021018.png)]

  • 最后执行query,通过statement去执行对应的sql语句,再用resultSetHandler.handleResultSets来处理获取到的结果,并返回

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IAhd2qr2-1637916210394)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126153352624.png)]


返回收尾流程

  • 执行完他会关闭closeStatement收尾操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MIABeWOY-1637916210394)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126154210203.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CheWRF7i-1637916210395)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126154536758.png)]

  • 返回我们数据库查到的数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vwPmSRh8-1637916210396)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126154951208.png)]

下面就是,我们上面执行链路执行完,一路一路的返回结果:↓

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TV6E426A-1637916210397)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126155010916.png)]

  • PageInterceptor.intercept

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WHJCigd8-1637916210397)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126155025140.png)]

  • Plugin.invoke

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hvgdGaVI-1637916210398)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126155100975.png)]

  • DefaultSqlSession.selectList

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FxeFUXiO-1637916210399)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126155157731.png)]

  • DefaultSqlSession.selectOne

这里就是判断他的结果是1个还是多个还是返回null

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e4gWCA3X-1637916210400)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126155238864.png)]

  • SqlSessionTemplate.SqlSessionInterceptor

提交commit了我们之前开的sqlsession的会话

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B40gdE3R-1637916210401)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126155623049.png)]

  • PageMapperMethod.execute

判断sql类型的地方

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ew50OfQg-1637916210401)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126162051228.png)]

  • PageMapperProxy.invoke

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3C86ebCE-1637916210402)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126162144469.png)]

  • 最后返回到了我们业务这里的代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3MuMcLlF-1637916210403)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211126162202607.png)]

至此,上面的一条sql执行的流程就算执行完毕了!


总结

阿昌这里记录下mybatis执行sql的流程

  • 刚开始被代理,去先判断他是否是Object类本身

  • 再判断缓存中是否有对应方法的缓存,如果缓存中没有,则创建一个,并添加到缓存中

  • 判断对应的sql执行类型,判断为select类型

  • 获取sqlSession对象,并打开sqlSession会话

  • 执行selectList,从mybtis配置类中的MappedStatement执行getMappedStatement(statement)拿到方法全限定名对应的sql信息

  • 执行Plugin类的mybatis提供第三方进行拦截的插件类的内容,如PageHelper等

  • 从本地缓存中拿

    • 有就从缓存中拿处理一下返回

    • 没有就去查数据库

      • 他会先给本地缓存先放一份这个内容,做占位符,

        • Key:为我们之前生成的CacheKey

        • Value:EXECUTION_PLACEHOLDER占位符

        • localCache.putObject(key, EXECUTION_PLACEHOLDER);

      • 根据对应类型的Executor来有不同的方案,查询数据库

        • 期间会拿到对应的Statement,和resultHandler结果处理器
        • 最后执行query,通过statement去执行对应的sql语句,再用resultSetHandler.handleResultSets来处理获取到的结果,并返回
        • 然后就是JDBC的内容,就不多说了
      • closeStatement关闭Statement

      • 根据queryStack,来是否清空本地缓存

    • 然后就是一路返回结果

      • 期间会提交sqlSession会话
      • 最后关闭sqlSession会话
  • 返回给业务结果


以上就是这次记录的全部内容,感谢你能看到这里!!!! ๑•̀ㅂ•́)

猜你喜欢

转载自juejin.im/post/7050703123920191519