Executor 的继承结构
每一个 SqlSession 都会拥有一个 Executor 对象,这个对象负责增删改查的具体操作,我们可以简单的将它理解为 JDBC 中 Statement 的封装版。也可以理解为 SQL 的执行引擎,要干活总得有一个发起人吧,可以把 Executor 理解为发起人的角色。
首先先从 Executor 的继承体系来认识一下
如上图所示,位于继承体系最顶层的是 Executor 执行器,它有两个实现类,分别是BaseExecutor
和 CachingExecutor
。
BaseExecutor
是一个抽象类,这种通过抽象的实现接口的方式是适配器设计模式之接口适配
的体现,是Executor 的默认实现,实现了大部分 Executor 接口定义的功能,降低了接口实现的难度。BaseExecutor 的子类有三个,分别是 SimpleExecutor、ReuseExecutor 和 BatchExecutor。
SimpleExecutor
: 简单执行器,是 MyBatis 中默认使用的执行器,每执行一次 update 或 select,就开启一个Statement 对象,用完就直接关闭 Statement 对象(可以是 Statement 或者是 PreparedStatment 对象)
ReuseExecutor
: 可重用执行器,这里的重用指的是重复使用 Statement,它会在内部使用一个 Map 把创建的Statement 都缓存起来,每次执行 SQL 命令的时候,都会去判断是否存在基于该 SQL 的 Statement 对象,如果存在 Statement 对象并且对应的 connection 还没有关闭的情况下就继续使用之前的 Statement 对象,并将其缓存起来。因为每一个 SqlSession 都有一个新的 Executor 对象,所以我们缓存在 ReuseExecutor 上的 Statement作用域是同一个 SqlSession。
BatchExecutor
: 批处理执行器,用于将多个 SQL 一次性输出到数据库
CachingExecutor
: 缓存执行器,先从缓存中查询结果,如果存在就返回之前的结果;如果不存在,再委托给Executor delegate 去数据库中取,delegate 可以是上面任何一个执行器。
Executor 的创建和选择
我们上面提到 Executor
是由 Configuration 创建的,Configuration 会根据执行器的类型创建,如下
这一步就是执行器的创建过程,根据传入的 ExecutorType
类型来判断是哪种执行器,如果不指定 ExecutorType ,默认创建的是简单执行器。它的赋值可以通过两个地方进行赋值:
- 可以通过
<settings>
标签来设置当前工程中所有的 SqlSession 对象使用默认的 Executor
<settings> <!--取值范围 SIMPLE, REUSE, BATCH --> <setting name="defaultExecutorType" value="SIMPLE"/> </settings>
- 另外一种直接通过Java对方法赋值的方式
session = factory.openSession(ExecutorType.BATCH);
Executor 的具体执行过程
Executor 中的大部分方法的调用链其实是差不多的,下面是深入源码分析执行过程,如果你没有时间或者暂时不想深入研究的话,给你下面的执行流程图作为参考。
我们紧跟着上面的 selectList
继续分析,它会调用到 executor.query
方法。
当有一个查询请求访问的时候,首先会经过 Executor 的实现类 CachingExecutor
,先从缓存中查询 SQL 是否是第一次执行,如果是第一次执行的话,那么就直接执行 SQL 语句,并创建缓存,如果第二次访问相同的 SQL 语句的话,那么就会直接从缓存中提取。
上面这段代码是从 selectList -> 从缓存中 query 的具体过程。可能你看到这里有些觉得类都是什么东西,我想鼓励你一下,把握重点,不用每段代码都看,从找到 SQL 的调用链路,其他代码想看的时候在看,看源码就是很容易发蒙,容易烦躁,但是切记一点,把握重点。
上面代码会判断缓存中是否有这条 SQL 语句的执行结果,如果没有的话,就再重新创建 Executor
执行器执行 SQL 语句,注意, list = doQuery
是真正执行 SQL 语句的过程,这个过程中会创建我们上面提到的三种执行器,这里我们使用的是简单执行器。
到这里,执行器所做的工作就完事了,Executor 会把后续的工作交给 StatementHandler
继续执行。下面我们来认识一下 StatementHandler
StatementHandler
StatementHandler
是四大组件中最重要的一个对象,负责操作 Statement 对象与数据库进行交互,在工作时还会使用 ParameterHandler
和 ResultSetHandler
对参数进行映射,对结果进行实体类的绑定,这两个组件我们后面说。
我们在搭建原生 JDBC 的时候,会有这样一行代码
Statement stmt = conn.createStatement(); //也可以使用PreparedStatement来做
这行代码创建的 Statement 对象或者是 PreparedStatement 对象就是由 StatementHandler 进行管理的。
StatementHandler 的继承结构
有没有感觉和 Executor
的继承体系很相似呢?最顶级接口是四大组件对象,分别有两个实现类 BaseStatementHandler
和 RoutingStatementHandler
,BaseStatementHandler 有三个实现类, 他们分别是 SimpleStatementHandler、PreparedStatementHandler 和 CallableStatementHandler。
RoutingStatementHandler
: RoutingStatementHandler 并没有对 Statement 对象进行使用,只是根据StatementType 来创建一个代理,代理的就是对应Handler的三种实现类。在MyBatis工作时,使用的StatementHandler 接口对象实际上就是 RoutingStatementHandler 对象。
BaseStatementHandler
: 是 StatementHandler 接口的另一个实现类,它本身是一个抽象类,用于简化StatementHandler 接口实现的难度,属于适配器设计模式体现,它主要有三个实现类
- SimpleStatementHandler: 管理 Statement 对象并向数据库中推送不需要预编译的SQL语句。
- PreparedStatementHandler: 管理 Statement 对象并向数据中推送需要预编译的SQL语句。
- CallableStatementHandler:管理 Statement 对象并调用数据库中的存储过程。
这里注意一下,SimpleStatementHandler 和 PreparedStatementHandler 的区别是 SQL 语句是否包含变量,是否通过外部进行参数传入。
SimpleStatementHandler 用于执行没有任何参数传入的 SQL
PreparedStatementHandler 需要对外部传入的变量和参数进行提前参数绑定和赋值。
”
StatementHandler 的创建和源码分析
我们继续来分析上面 query
的调用链路,StatementHandler 的创建过程如下
MyBatis 会根据 SQL 语句的类型进行对应 StatementHandler 的创建。我们以预处理 StatementHandler 为例来讲解一下
执行器不仅掌管着 StatementHandler 的创建,还掌管着创建 Statement 对象,设置参数等,在创建完 PreparedStatement 之后,我们需要对参数进行处理了。
如果用一副图来表示一下这个执行流程的话我想是这样
这里我们先暂停一下,来认识一下第三个核心组件 ParameterHandler
ParameterHandler
</div>