Mybatis流程分析(七): 探寻Mybatis中执行sql语句的"入口"
本系列文章皆在从细节着手,由浅入深的分析Mybatis框架内部的处理逻辑,带你从一个全新的角度来认识Mybatis的工作原理。
思考,输出,沉淀。用通俗的语言陈述技术,让自己和他人都有所收获。 作者:毅航😜
前言
在前几章:
中我们利用大量的篇幅详细介绍了Mybatis中Mapper.xml配置的sql语句与接口中方法绑定的具体细节,并分析了Mybatis中根据接口构建实例对象的背后逻辑。事实上,前几章的核心内容通过如下这张图就能概括。

在几章的介绍中,我们一直都在围绕使用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中代码那么多,如果要分析Mybatis中sql执行的相关逻辑,应该从哪入手呢?
接下来不妨按着笔者的思路来逐一分析,看看笔者是如何来寻找Mybatis中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));
}
}
简单来看,MapprProxy中的invoke方法会将执行sql的功能委托给MapperMethodInvoker。进一步,MapperMethodInvoker内部又会将处理逻辑委托给MapperMethod中execute()方法进行处理。
事实上,Mybatis中通过代理返回的实例对象,其增强逻辑本质就是交给MapperProxy来进行完成的。具体来看,增强逻辑本质就是在的MapperProxy中的invoke方法来完成的。
进一步,当调用Mybatis所返回的实例对象的内部方法时,其本质就是执行与方法绑定的sql语句。换言之,只要顺着MapperProxy中的invoke方法的调用链寻找,就一定能找到Mybatis中执行sql的相关逻辑。
更进一步,相信MyBatis中的 SqlSession 应该有一个更深刻的认识。其功能不仅仅只局限于会话管理和对象生成,其内部更是提供了许多方法来执行 sql 的操作,这其中包括 select 方法。
至此,我们先前提出的问题其实已经有了答案。换言之,若要探究Mybatis中sql执行的相关逻辑,则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方法内部会做两件事:
- 根据传递的
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方法。
换言之,Mybatis中sql语句的相关执行都是通过Executor来完成。事实上,MyBatis 中的 Executor 是一个核心组件,负责执行sql语句与数据库的交互。它扮演着连接数据库、执行sql语句、处理结果集等重要角色。而执行器——Executor也将在下一章进行讨论,敬请期待。
转载自:https://juejin.cn/post/7273875079657947175