关于java:Mybatis如何执行Select语句你真的知道吗

38次阅读

共计 3745 个字符,预计需要花费 10 分钟才能阅读完成。

继续原创输入,点击上方蓝字关注我吧

作者:不才陈某

博客: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 的执行流程,如果有谬误之处欢送斧正,如果感觉陈某写得不错,有所播种,关注分享一波。
正文完
 0