likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 24

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

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


前言

在前几章:

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

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

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

使用Mybatis的四行代码

  //<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


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));
  }

}

简单来看,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方法中的"秘密"

DefaultSqlSession#select


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也将在下一章进行讨论,敬请期待。

转载自:https://juejin.cn/post/7273875079657947175
评论
请登录