写点什么

Mybatis 如何执行 Select 语句,你真的知道吗?

  • 2021 年 11 月 11 日
  • 本文字数:1632 字

    阅读完需:约 5 分钟



  • 本篇文章讲的一切内容都是基于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:![](https://img-service.csdnimg.cn/img_convert/0e4ab00448d5dd8b7ee3f


【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


551418394cf.png)


  • 根据上图源码的分析,其实 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将其转换成相应类型的参数。

评论

发布
暂无评论
Mybatis如何执行Select语句,你真的知道吗?