Mybatis流程分析(七): 探寻Mybatis中执行sql语句的”入口”

2023年 9月 5日 40.8k 0

本系列文章皆在从细节着手,由浅入深的分析Mybatis框架内部的处理逻辑,带你从一个全新的角度来认识Mybatis的工作原理。

思考,输出,沉淀。用通俗的语言陈述技术,让自己和他人都有所收获。
作者:毅航😜

前言

在前几章:

  • Mybatis流程分析(五): sql语句与接口中方法绑定的"细节"
  • Mybatis流程分析(四):Mybatis构建Mapper背后的故事
  • Mybatis流程分析(六): Mybatis中方法和sql语句的桥梁——MapperProxy
  • 中我们利用大量的篇幅详细介绍了MybatisMapper.xml配置的sql语句与接口中方法绑定的具体细节,并分析了Mybatis中根据接口构建实例对象的背后逻辑。事实上,前几章的核心内容通过如下这张图就能概括。

    image.png

    在几章的介绍中,我们一直都在围绕使用Mybatis的四行代码进行分析。但这就结束了吗?当然不是!这四行代码是你“简单”使用Mybatis的极限,却不是Mybatis的极限。

    使用Mybatis的四行代码

      // 加载配置文件
      InputStream is = Resources.getResourceAsStream("mybatis.xml");
      // 创建sessionFactory对象
      sessionFactory = new SqlSessionFactoryBuilder().build(is);
      // 获取sqlSession对象信息
      SqlSession session = factoy.openSqlSession();
      // 构建映射器的代理对象
      UserMapper mapper = session.getMapper(UserMapper.class);
      // .....调用相关方法信息
    

    事实上,这四行代码是Mybatis提供给开发者方便其快速上手Mybatis的。通过这样的编码,开发者就能实现接口方法和sql语句的绑定,进而获取到一个实现该接口的实例对象。进一步,用户调用接口中的方法将就能完成sql的执行。

    Mybatis内部又是如何来执行这些sql语句的呢?别着急,这些内容都将在后续的文章都将会进行介绍。笔者将像剥洋葱一样,一层层的剥开潜藏在Mybatis身上的全部秘密

    此时你可能会疑惑Mybatis中代码那么多,如果要分析Mybatissql执行的相关逻辑,应该从哪入手呢?

    接下来不妨按着笔者的思路来逐一分析,看看笔者是如何来寻找Mybatissql执行的入口位置的,相信看到最后你一定会有一种“拨云见日”的感觉。

    重新认识SqlSession

    在Mybatis流程分析(六): Mybatis中方法和sql语句的桥梁——MapperProxy我们曾提到:当调用Mybaits内部经过动态带来返回的实例对象中的方式时,其本质是从Configurtaion对象中获取缓存的MappedStatement对象,提取出其中的sql信息,然后将sql执行逻辑委托于SqlSession来进行执行。 具体来看,这部分代码逻辑如下:

    
    public class MapperMethod {
    
    // .... 省略其他无关代码
    
      public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        switch (command.getType()) {
         
          case SELECT:
            if (method.returnsVoid() && method.hasResultHandler()) {
              executeWithResultHandler(sqlSession, args);
            }
          // ....省略其他相似逻辑的代码
        return result;
      }
    
    
    private void executeWithResultHandler(SqlSession sqlSession, 
                                            Object[] args) {
        // 相当于对sql内容进行封装
        MappedStatement ms = sqlSession.getConfiguration().
        Object param = method.convertArgsToSqlCommandParam(args);
        
        // 通过sqlSession中的select方法进行执行
        sqlSession.select(command.getName(), param, method.extractResultHandler(args));
      }
    
    }
    

    至于Mybatis内部是如何调用到MapperMethod中的executeWithResultHandler方法的,笔者在此便不再重复论述了。不了解其中调用逻辑的可以回看Mybatis流程分析(六): Mybatis中方法和sql语句的桥梁——MapperProxy中的相关内容。

    简单来看,MapprProxy中的invoke方法会将执行sql的功能委托给MapperMethodInvoker。进一步,MapperMethodInvoker内部又会将处理逻辑委托给MapperMethod中execute()方法进行处理。

    事实上,Mybatis中通过代理返回的实例对象,其增强逻辑本质就是交给MapperProxy来进行完成的。具体来看,增强逻辑本质就是在的MapperProxy中的invoke方法来完成的。

    进一步,当调用Mybatis所返回的实例对象的内部方法时,其本质就是执行与方法绑定的sql语句。换言之,只要顺着MapperProxy中的invoke方法的调用链寻找,就一定能找到Mybatis中执行sql的相关逻辑。

    更进一步,相信MyBatis中的 SqlSession 应该有一个更深刻的认识。其功能不仅仅只局限于会话管理对象生成,其内部更是提供了许多方法来执行 sql 的操作,这其中包括 select 方法。

    至此,我们先前提出的问题其实已经有了答案。换言之,若要探究Mybatissql执行的相关逻辑,则SqlSession中的select方法就是我们分析这一问题入口

    藏在select方法中的"秘密"

    我们曾在Mybatis流程分析(二):配置处理,构建SqlSession工厂中提及过,在MybatisSqlSession为一个接口。而我们使用Mybatis时,其内部会为我们构建一个DefaultSqlSession对象来进行会话的管理。所以,如果要分析Sqlsessionselect方法的实现逻辑,我们重点关注其在DefaultSqlSession中的实现就可以了。

    DefaultSqlSession#select

    
    public void select(String statement, Object parameter, 
                        RowBounds rowBounds, ResultHandler handler) {
          根据namespce+methodId获取对应的MappedStatement
        MappedStatement ms = configuration.getMappedStatement(statement);
         调用执行器中Executor中的query方法
        executor.query(ms, wrapCollection(parameter), rowBounds, handler);
        // .... 省略其他无关逻辑
     
    }
    

    可以看到,DefaultSqlSession中的select方法内部会做两件事:

  • 根据传递的statmentId信息从Configuration中获取对应的MappedStatment对象;
  • 将获取到的MappedStatment对象作为参数传递给 executor.query
  • 细心的读者可能会注意到上述代码中所示的select方法其返回值为一个void,如果我们接口中方法的返回值类型为为一个实体对象,那这样不就出错吗?

    但实际的结果却和预期相反,在使用Mybatis时并不会出现这样的bug。至于其中的原因,笔者在此先不给出相关解答,这个问题的答案我们留在分析ResultHandler的时候给予回答。

    (注:DefaultSqlSession中存在select方法的多个重载版本,但其最终都会调用到上述这个版本的select方法)

    总结

    本文从MapperProxy中的invoke方法入手,逐一梳理invoke方法的调用逻辑,由浅入深的分析了Mybaits内部执行sql的语句的入口位置。具体来看,Mybatis中执行sql语句会委托给SqlSession中的select。进一步,sql语句的执行又将委托给Executor中的query方法。

    换言之,Mybatissql语句的相关执行都是通过Executor来完成。事实上,MyBatis 中的 Executor 是一个核心组件,负责执行sql语句与数据库的交互。它扮演着连接数据库、执行sql语句、处理结果集等重要角色。而执行器——Executor也将在下一章进行讨论,敬请期待。

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论