主要讲解MyBatis中SQL的执行流程,基于MyBatis的基础知识进行更深层次的剖析。
在《【MyBatis系列1】基础知识(上)》中,我们讲解了MyBaits的工作原理,以及它的四大核心组件的使用姿势,包括SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession和SQL Mapper。在《【MyBatis系列1】基础知识(下)》中,通过完整的MayBatis使用示例,详细讲解了MyBatis的XML配置文件。
所以在阅读该文章前,建议大家先看《【MyBatis系列1】基础知识》上-下两篇文章,这篇文章主要是基于SQL的执行流程,对MyBatis的基础知识进行更深层次的讲解,涉及到之前已经讲过的基础知识会直接跳过。
MyBatis 最上面是接口层,接口层就是开发人员在 Mapper 或者是 Dao 接口中的接口定义,是查询、新增、更新还是删除操作;中间层是数据处理层,主要是配置 Mapper -> XML 层级之间的参数映射,SQL 解析,SQL 执行,结果映射的过程。上述两种流程都由基础支持层来提供功能支撑,基础支持层包括连接管理,事务管理,配置加载,缓存处理等。
在不与Spring 集成的情况下,使用 MyBatis 执行数据库的操作主要如下:
InputStream is = Resources.getResourceAsStream("myBatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
sqlSession = factory.openSession();
其中的SqlSessionFactory,SqlSession是 MyBatis 接口的核心类,尤其是 SqlSession,这个接口是MyBatis 中最重要的接口,这个接口能够让你执行命令,获取映射,管理事务。
这块知识的扩展,可以参考《【MyBatis系列1】基础知识(上)》
在 Mybatis 初始化过程中,会加载 mybatis-config.xml 配置文件、映射配置文件以及 Mapper 接口中的注解信息,解析后的配置信息会形成相应的对象并保存到 Configration 对象中。之后,根据该对象创建SqlSessionFactory 对象。待 Mybatis 初始化完成后,可以通过 SqlSessionFactory 创建 SqlSession 对象并开始数据库操作。
这块知识的扩展,可以参考《【MyBatis系列1】基础知识(上)》
Mybatis 实现的动态 SQL 语句,几乎可以编写出所有满足需要的 SQL。
Mybatis 中 scripting 模块会根据用户传入的参数,解析映射文件中定义的动态 SQL 节点,形成数据库能执行的SQL 语句。
SQL 语句的执行涉及多个组件,包括 MyBatis 的四大核心,它们是: Executor、StatementHandler、ParameterHandler、ResultSetHandler。SQL 的执行过程可以用下面这幅图来表示:
MyBatis 层级结构各个组件的介绍:
这个也是摘录网上博客,由于该包括主要讲源码,我会剔除源码的部分,只保留内容的讲解流程。
SqlSessionFactory 有两个实现类,一个是 SqlSessionManager 类,一个是 DefaultSqlSessionFactory 类:
下面来对 SqlSessionFactory 的执行流程来做一个分析,首先第一步是 SqlSessionFactory 的创建:
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
从这行代码入手,首先创建了一个 SqlSessionFactoryBuilder 工厂,这是一个建造者模式的设计思想,由 builder 建造者来创建 SqlSessionFactory 工厂
然后调用 SqlSessionFactoryBuilder 中的 build 方法传递一个InputStream 输入流,Inputstream 输入流中就是你传过来的配置文件 mybatis-config.xml,SqlSessionFactoryBuilder 根据传入的 InputStream 输入流和environment、properties属性创建一个XMLConfigBuilder对象。
通过XML文件生成source -> 通过source中的数据生成SqlSessionFactoryBuilder -> 得到SqlSessionFactory -> 拿到SqlSession,XML配置中的字段含义可以参考《【MyBatis系列1】基础知识(下)》。
通过 SqlSessionFactory 对象得到 SqlSession,然后就可以执行 SQL 语句了。在 SqlSessionFactory.openSession 过程中,它会调用到 DefaultSqlSessionFactory 中的 openSessionFromDataSource 方法,这个方法主要创建了两个与我们分析执行流程重要的对象,一个是 Executor 执行器对象,一个是 SqlSession 对象。
SqlSession 对象是 MyBatis 中最重要的一个对象,这个接口能够让你执行命令,获取映射,管理事务。SqlSession 中定义了一系列模版方法,让你能够执行简单的 CRUD 操作,也可以通过 getMapper 获取 Mapper 层,执行自定义 SQL 语句,因为 SqlSession 在执行 SQL 语句之前是需要先开启一个会话,涉及到事务操作,所以还会有 commit、 rollback、close 等方法。这也是模版设计模式的一种应用。
举个栗子,SqlSession 控制数据库事务的方法,如下所示:
//定义 SqlSession
SqlSession sqlSession = null;
try {
// 打开 SqlSession 会话
sqlSession = SqlSessionFactory.openSession();
// some code...
sqlSession.commit(); // 提交事务
} catch (IOException e) {
sqlSession.rollback(); // 回滚事务
}finally{
// 在 finally 语句中确保资源被顺利关闭
if(sqlSession != null){
sqlSession.close();
}
}
MapperProxy 是 Mapper 映射 SQL 语句的关键对象,我们写的 Dao 层或者 Mapper 层都是通过 MapperProxy 来和对应的 SQL 语句进行绑定的。
这就是 MyBatis 的核心绑定流程,我们可以看到 SqlSession 首先调用 getMapper 方法。SqlSession 是大哥级别的人物,只定义标准(有一句话是怎么说的来着,一流的企业做标准,二流的企业做品牌,三流的企业做产品)
SqlSession 不愿意做的事情交给 Configuration 这个手下去做,但是 Configuration 也是有小弟的,它不愿意做的事情直接甩给小弟去做,这个小弟是谁呢?它就是 MapperRegistry,马上就到核心部分了。MapperRegistry 相当于项目经理,项目经理只从大面上把握项目进度,不需要知道手下的小弟是如何工作的,把任务完成了就好。最终真正干活的还是 MapperProxyFactory。看到这段代码 Proxy.newProxyInstance ,你是不是有一种恍然大悟的感觉,如果你没有的话,建议看一下这篇文章《【设计模式系列6】代理模式》。
也就是说,MyBatis 中 Mapper 和 SQL 语句的绑定正是通过动态代理来完成的。
MapperProxyFactory 会生成代理对象,这个对象就是 MapperProxy,最终会调用到 mapperMethod.execute 方法,execute逻辑比较简单,就是判断是 插入、更新、删除 还是 查询 语句。
这里饶了一大圈,说的简单一点,其实就是通过sqlSession一层层拿到MapperProxyFactory,然后拿到MapperProxy,最后调用SQL操作接口时,其实是通过MapperProxy中封装的execute()来执行的,最后其实是走到了Executor中的逻辑。
每一个 SqlSession 都会拥有一个 Executor 对象,这个对象负责增删改查的具体操作,我们可以简单的将它理解为 JDBC 中 Statement 的封装版。也可以理解为 SQL 的执行引擎,要干活总得有一个发起人吧,可以把 Executor 理解为发起人的角色。
执行器的创建类型是通过MyBatis XML文件配置的:
<settings>
<!--取值范围 SIMPLE, REUSE, BATCH -->
<setting name="defaultExecutorType" value="SIMPLE"/>
</settings>
不同的执行器介绍如下:
Executor 的具体执行过程:
执行器所做的工作就完事了,Executor 会把后续的工作交给 StatementHandler 继续执行。下面我们来认识一下 StatementHandler
通过MapperProxy找到操作接口后,就开始通过Executor去执行具体的逻辑,然后交给StatementHandler去执行。
StatementHandler 是四大组件中最重要的一个对象,负责操作 Statement 对象与数据库进行交互,在工作时还会使用 ParameterHandler 和 ResultSetHandler对参数进行映射,对结果进行实体类的绑定。
StatementHandler 的继承结构:
MyBatis 会根据 SQL 语句的类型进行对应 StatementHandler 的创建,在创建完 PreparedStatement 之后,我们需要对参数进行处理了,参数处理通过ParameterHandler进行。
ParameterHandler 相比于其他的组件就简单很多了,ParameterHandler 译为参数处理器,负责为 PreparedStatement 的 sql 语句参数动态赋值,它实现了这两个方法:
MyBatis 只有一个默认的实现类就是 DefaultResultSetHandler,DefaultResultSetHandler 主要负责处理两件事:
在 DefaultResultSetHandler 中处理完结果映射,并把上述结构返回给调用的客户端,从而执行完成一条完整的SQL语句。
通过这篇文章,可以初步了解MyBatis执行逻辑的全貌,大家可以作为兴趣点学习,也可以作为面试考点。之前感觉对MyBatis还比较模糊,特别是看基础知识时,里面一堆对象跳来跳去,一直有种没有完全看懂的感觉,现在全部梳理一遍后,整个流程就清晰很多。
关于MyBatis基础知识的讲解就到这里,后面打算再写一篇MyBatis和Spring Boost集成的内容,然后再将小米这边用到MyBatis项目中的知识抽离出来,作为这个系列的完结。相信经过这2周的学习,项目中用到MyBatis相关的知识,应该就不会成为我看代码和开发的瓶颈了。
参考博客:https://mp.weixin.qq.com/s?__biz=MzkwMDE1MzkwNQ==&mid=2247496022&idx=1&sn=bdf49a3936ea93b4ff351720961c988f&chksm=c04ae608f73d6f1e9b296cb6aeb73118b64c48fe96c094a130f9d52152b3847529e70f31e128&token=1511605423&lang=zh_CN#rd
欢迎大家多多点赞,更多文章,请关注微信公众号“楼仔进阶之路”,点关注,不迷路~~