本文源代码来源于mybatis-spring-boot-starter的2.1.2版本
SQL语句的执行涉及各个组件,其中比较重要的是Executor
,StatementHandler
,ParameterHandler
和ResultSetHandler
。
Executor
对象在创建Configuration
对象的时候创建,并且缓存在Configuration
对象里,负责管理一级缓存和二级缓存,并提供是事务管理的相关操作。Executor
对象的主要功能是调用StatementHandler
访问数据库,并将查询结果存入缓存中(如果配置了缓存的话)。StatementHandler
首先通过ParammeterHandler
完成SQL的实参绑定,然后通过java.sql.Statement
对象执行sql语句并得到结果集ResultSet
,最后通过ResultSetHandler
完成结果集的映射,得到对象并返回。
Executor
1.1 类图
每一个 SqlSession
都会拥有一个Executor
对象,这个对象负责增删改查的具体操作,我们可以简单的将它理解为 JDBC 中 Statement 的封装版。也可以理解为 SQL 的执行引擎,要干活总得有一个发起人吧,可以把 Executor 理解为发起人的角色。
如上图所示,位于继承体系最顶层的是Executor
执行器,它有两个实现类,分别是和BaseExecutor
和CachingExecutor
。
BaseExecutor
BaseExecutor
是一个抽象类,这种通过抽象的实现接口的方式是的体现,是Executor 的默认实现,实现了大部分 Executor 接口定义的功能,关于查询更新的具体实现由其子类实现。️BaseExecutor 的子类有四个,分别是 SimpleExecutor
、ReuseExecutor
、CloseExecutor
和 BatchExecutor
。
SimpleExecutor
简单执行器,是 MyBatis 中默认使用的执行器,每执行一次 update 或 select,就开启一个Statement
对象,用完就直接关闭 Statement
对象(可以是 Statement 或者是 PreparedStatment 对象)
ReuseExecutor
可重用执行器,这里的重用指的是重复使用Statement
,它会在内部使用一个 Map 把创建的Statement
都缓存起来,每次执行 SQL 命令的时候,都会去判断是否存在基于该 SQL 的 Statement
对象,如果存在Statement
对象并且对应的 connection
还没有关闭的情况下就继续使用之前的 Statement
对象,并将其缓存起来。因为每一个 SqlSession
都有一个新的 Executor
对象,所以我们缓存在 ReuseExecutor
上的 Statement
作用域是同一个 SqlSession
。
CloseExecutor
表示一个已关闭的Executor执行期
BatchExecutor
批处理执行器,用于将多个 SQL 一次性输出到数据库
CachingExecutor
缓存执行器,先从缓存中查询结果,如果存在就返回之前的结果;如果不存在,再委托给Executor delegate
去数据库中取,delegate 可以是上面任何一个执行器。
1.2 Executor的选择和创建
1 | 复制代码private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { |
SqlSessionFactory通过Configuration来创建sqlSession,Executor也是在这个时候初始化,我们来看下configuration.newExecutor()
这个方法:
1 | 复制代码 public Executor newExecutor(Transaction transaction, ExecutorType executorType) { |
ExecutorType类型可以通过xml标签和JavaApi进行赋值,默认为ExecutorType.SIMPLE
。
Mybatis插件机制会在其他系列文章里面讲解,这里就不过多介绍了。
1.3 Executor的执行流程
我们从SqlSession的selectList方法入手,其实他们的调用链路都差不多。
1 | 复制代码@Override |
上面说到cacheEnabled默认为true,则默认创建的Executor为CachingExecutor,并且其内部包裹着SimpleExecutor。所以这里executor还是CachingExecutor,我们来看下CachingExecutor
的实现
1 | 复制代码 @Override |
这里的delegate
就是SimpleExecutor
,也就是说最终还是会执行SimpleExecutor
的query实现。到这里,执行器所做的工作就完事了,Executor
会把后续的工作交给继续执行。下面我们来认识一下StatementHandler
。
StatementHandler
StatementHandler主要负责操作Statement对象与数据库进行交互,在这里我们先回顾一下原生JDBC的相关知识:
1 | 复制代码 //1 注册驱动 |
JDBC通过java.sql.Connection
对象创建Statement
对象,Statement
对象的execute
方法就是执行SQL语句的入口。那么对于Mybtais的StatementHandler
是用于管理Statement
对象的。
2.1 类图
有木有感觉这个继承关系和Executor极为相似,顶层负接口分别有两个实现BaseStatementHandler
和RoutingStatementHandler
,而BaseStatementHandler分别有三个实现类SimpleStatementHandler
、PreparedStatementHandler
、CallableStatementHandler
。
我们不妨先来看下StatementHandler
定义的方法:
1 | 复制代码ublic interface StatementHandler { |
BaseStatementHandler
它本身是一个抽象类,用于简化StatementHandler 接口实现的难度,属于适配器设计模式体现,它主要有三个实现类:
SimpleStatementHandler
java.sql.Statement对象创建处理器,管理 Statement 对象并向数据库中推送不需要预编译的SQL语句。
PreparedStatementHandler
java.sql.PrepareStatement对象的创建处理器,管理Statement对象并向数据中推送需要预编译的SQL语句。
注意:SimpleStatementHandler
和 PreparedStatementHandler
的区别是 SQL 语句是否包含变量。是否通过外部进行参数传入。SimpleStatementHandler
用于执行没有任何参数传入的 SQL,PreparedStatementHandler
需要对外部传入的变量和参数进行提前参数绑定和赋值。
CallableStatementHandler
java.sql.CallableStatement对象的创建处理器,管理 Statement 对象并调用数据库中的存储过程。
RoutingStatementHandler
1 | 复制代码 public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { |
与CachExecutor
相似,RoutingStatementHandler
并没有对 Statement
对象进行使用,只是根据StatementType 来创建一个代理,代理的就是对应Handler的三种实现类。在MyBatis工作时,使用的StatementHandler 接口对象实际上就是 RoutingStatementHandler
对象。
2.2 StatementHandler的选择和创建
以查询为例,前面说到Executor在执行时会先查询缓存在走数据库,我们顺着**queryFromDatabase()方法的doQuery()**方法可以发现:
1 | 复制代码 @Override |
2.2.1 configuration.newStatementHandler
1 | 复制代码 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { |
这里会创建一个RoutingStatementHandler
对象,我们刚才说过了,它会根据StatementType来创建对应的Statement对象。StatementType是MappedStatement
的一个属性,他在bulid的时候默认为StatementType.PREPARED
。PreparedStatementHandler
在构建的时候会调用其父类BaseStatementHandlerde
的构造函数。ParameterHandler
和ResultSetHandler
也是在这里创建的,我们来看看。
2.2.2 BaseStatementHandler
1 | 复制代码 protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { |
ParameterHandler
3.1 类图
相比于其他的组件就简单很多了,ParameterHandler 译为参数处理器,负责为 PreparedStatement 的 sql 语句参数动态赋值,这个接口很简单只有两个方法:
- getParameterObject:用于读取参数
- setParameters: 用于对
PreparedStatement
的参数赋值
3.2 ParameterHandler的创建
1 | 复制代码public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { |
ResultSetHandler
4.1 类图
ResultSetHandler
也很简单,它只有一个实现类DefaultResultSetHandler
,主要负责处理两件事
- 处理 Statement 执行后产生的结果集,生成结果列表。
- 处理存储过程执行后的输出参数
4.2 ResultSetHandler的创建
1 | 复制代码public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, |
本文仅对Mybatis执行sql的组件做一个入门级的介绍,后面我们会对执行sql的过程作出详细的讲解。
本文转载自: 掘金