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

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

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

作者:毅航😜


前言

在前几章:

  1. Mybatis流程分析(五): sql语句与接口中方法绑定的"细节"
  2. Mybatis流程分析(四):Mybatis构建Mapper背后的故事
  3. Mybatis流程分析(六): Mybatis中方法和sql语句的桥梁------MapperProxy

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

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

使用Mybatis的四行代码

java 复制代码
  //<1> 加载配置文件
  InputStream is = Resources.getResourceAsStream("mybatis.xml");
  //<2> 创建sessionFactory对象
  sessionFactory = new SqlSessionFactoryBuilder().build(is);
  //<3> 获取sqlSession对象信息
  SqlSession session = factoy.openSqlSession();
  //<4> 构建映射器的代理对象
  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来进行执行。 具体来看,这部分代码逻辑如下:

java 复制代码
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

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

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

  1. 根据传递的statmentId信息从Configuration中获取对应的MappedStatment对象;
  2. 将获取到的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也将在下一章进行讨论,敬请期待。

相关推荐
javaTodo3 分钟前
Claude Code 记忆机制详解:从 CLAUDE.md 到 Auto Memory,六层体系全拆解
后端
LSTM9724 分钟前
使用 C# 和 Spire.PDF 从 HTML 模板生成 PDF 的实用指南
后端
JaguarJack35 分钟前
为什么 PHP 闭包要加 static?
后端·php·服务端
BingoGo1 小时前
为什么 PHP 闭包要加 static?
后端
是糖糖啊1 小时前
OpenClaw 从零到一实战指南(飞书接入)
前端·人工智能·后端
百度Geek说1 小时前
基于Spark的配置化离线反作弊系统
后端
后端AI实验室2 小时前
用AI写代码,我差点把漏洞发上线:血泪总结的10个教训
java·ai
Java编程爱好者2 小时前
虚拟线程深度解析:轻量并发编程的未来趋势
后端
苏三说技术2 小时前
Spring AI 和 LangChain4j ,哪个更好?
后端