mybatis 执行sql_MyBatis源码分析 - SQL执行过程(二)之 StatementHandler

ed704b9e9b66090676910bae33786cdd.png

在前面一系列的文档中,我已经分析了 MyBatis 的基础支持层以及整个的初始化过程,此时 MyBatis 已经处于就绪状态了,等待使用者发号施令了 拉勾IT课小编为大家分解

那么接下来我们来看看它执行SQL的整个过程,该过程比较复杂,涉及到二级缓存,将返回结果转换成 Java 对象以及延迟加载等等处理过程,这里将一步一步地进行分析:

• 《SQL执行过程(一)之Executor》

• 《SQL执行过程(二)之StatementHandler》

• 《SQL执行过程(三)之ResultSetHandler》

• 《SQL执行过程(四)之延迟加载》

MyBatis中SQL执行的整体过程如下图所示:

b54e669c368b6f8cb1c51ce399be6ecd.png

在 SqlSession 中,会将执行 SQL 的过程交由Executor执行器去执行,过程大致如下:

1. 通过DefaultSqlSessionFactory创建与数据库交互的 SqlSession “会话”,其内部会创建一个Executor执行器对象

2. 然后Executor执行器通过StatementHandler创建对应的java.sql.Statement对象,并通过ParameterHandler设置参数,然后执行数据库相关操作

3. 如果是数据库更新操作,则可能需要通过KeyGenerator先设置自增键,然后返回受影响的行数

4. 如果是数据库查询操作,则需要将数据库返回的ResultSet结果集对象包装成ResultSetWrapper,然后通过DefaultResultSetHandler对结果集进行映射,最后返回 Java 对象

上面还涉及到一级缓存、二级缓存和延迟加载等其他处理过程

SQL执行过程(二)之StatementHandler

在上一篇文档中,已经详细地分析了在MyBatis的SQL执行过程中,SqlSession会话将数据库操作交由Executor执行器去完成,实际上需要通过StatementHandler创建相应的Statement对象,并做一些准备工作,然后通过Statement执行数据库操作,查询结果则需要通过ResultSetHandler对结果集进行映射转换成Java对象,那么接下来我们先来看看StatementHandler到底做哪些操作

StatementHandler接口的实现类如下图所示:

• org.apache.ibatis.executor.statement.RoutingStatementHandler:实现StatementHandler接口,装饰器模式,根据Statement类型创建对应的StatementHandler对象,所有的方法执行交由该对象执行

• org.apache.ibatis.executor.statement.BaseStatementHandler:实现StatementHandler接口,提供骨架方法,指定的几个抽象方法交由不同的子类去实现

• org.apache.ibatis.executor.statement.SimpleStatementHandler:继承BaseStatementHandler抽象类,创建java.sql.Statement进行数据库操作

• org.apache.ibatis.executor.statement.PreparedStatementHandler:继承BaseStatementHandler抽象类,创建java.sql.PreparedStatement进行数据库操作(默认)

• org.apache.ibatis.executor.statement.CallableStatementHandler:继承BaseStatementHandler抽象类,创建java.sql.CallableStatement进行数据库操作,用于存储过程

我们先回顾一下StatementHandler是在哪里被创建的,可以在《SQL执行过程(一)之Executor》的SimpleExecutor小节中有讲到,创建的是RoutingStatementHandler对象

StatementHandler

org.apache.ibatis.executor.statement.StatementHandler:Statement处理器接口,代码如下:

public interface StatementHandler {

/**

* 准备操作,可以理解成创建 Statement 对象

*

* @param connection Connection 对象

* @param transactionTimeout 事务超时时间

* @return Statement 对象

*/

Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;

/**

* 设置 Statement 对象的参数

*

* @param statement Statement 对象

*/

void parameterize(Statement statement) throws SQLException;

/**

* 添加 Statement 对象的批量操作

*

* @param statement Statement 对象

*/

void batch(Statement statement) throws SQLException;

/**

* 执行写操作

*

* @param statement Statement 对象

* @return 影响的条数

*/

int update(Statement statement) throws SQLException;

/**

* 执行读操作

*

* @param statement Statement 对象

* @param resultHandler ResultHandler 对象,处理结果

* @param <E> 泛型

* @return 读取的结果

*/

<E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;

/**

* 执行读操作,返回 Cursor 对象

*

* @param statement Statement 对象

* @param <E> 泛型

* @return Cursor 对象

*/

<E> Cursor<E> queryCursor(Statement statement) throws SQLException;

/**

* @return BoundSql 对象

*/

BoundSql getBoundSql();

/**

* @return ParameterHandler 对象

*/

ParameterHandler getParameterHandler();

}

每个方法可以根据注释先理解它的作用,在实现类中的会讲到

RoutingStatementHandler

org.apache.ibatis.executor.statement.RoutingStatementHandler:实现StatementHandler接口,采用装饰器模式,在初始化的时候根据Statement类型,创建对应的StatementHandler对象,代码如下:

public class RoutingStatementHandler implements StatementHandler {

private final StatementHandler delegate;

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,

ResultHandler resultHandler, BoundSql boundSql) {

// 根据不同的类型,创建对应的 StatementHandler 实现类

switch (ms.getStatementType()) {

case STATEMENT:

delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);

break;

case PREPARED:

delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);

break;

case CALLABLE:

delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);

break;

default:

throw new ExecutorException("Unknown statement type: " + ms.getStatementType());

}

}

}

• 在构造函数中初始化delegate委托对象,根据MappedStatement(每个SQL对应的对象)的statementType类型,创建对应的StatementHandler实现类

• 其余所有的方法都是直接交由delegate去执行的,这里就不列出来了,就是实现StatementHandler接口的方法

回顾到《MyBatis初始化(二)之加载Mapper接口与XML映射文件》中的XMLStatementBuilder小节,在parseStatementNode方法中的第10步如下:

StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));

所以说statementType的默认值为PREPARED,委托对象也就是PreparedStatementHandler类型

BaseStatementHandler

org.apache.ibatis.executor.statement.BaseStatementHandler:实现StatementHandler接口,提供骨架方法,指定的几个抽象方法交由不同的子类去实现

构造方法

public abstract class BaseStatementHandler implements StatementHandler {

/**

* 全局配置

*/

protected final Configuration configuration;

/**

* 实例工厂

*/

protected final ObjectFactory objectFactory;

/**

* 类型处理器注册表

*/

protected final TypeHandlerRegistry typeHandlerRegistry;

/**

* 执行结果处理器

*/

protected final ResultSetHandler resultSetHandler;

/**

* 参数处理器,默认 DefaultParameterHandler

*/

protected final ParameterHandler parameterHandler;

/**

* 执行器

*/

protected final Executor executor;

/**

* SQL 相关信息

*/

protected final MappedStatement mappedStatement;

/**

* 分页条件

*/

protected final RowBounds rowBounds;

/**

* SQL 语句

*/

protected BoundSql boundSql;

protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject,

RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

this.configuration = mappedStatement.getConfiguration();

this.executor = executor;

this.mappedStatement = mappedStatement;

this.rowBounds = rowBounds;

this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();

this.objectFactory = configuration.getObjectFactory();

// <1> 如果 boundSql 为空,更新数据库的操作这里传入的对象会为 null

if (boundSql == null) { // issue #435, get the key before calculating the statement

// <1.1> 生成 key,定义了 <selectKey /> 且配置了 order="BEFORE",则在 SQL 执行之前执行

generateKeys(parameterObject);

// <1.2> 创建 BoundSql 对象

boundSql = mappedStatement.getBoundSql(parameterObject);

}

this.boundSql = boundSql;

// <2> 创建 ParameterHandler 对象,默认为 DefaultParameterHandler

// PreparedStatementHandler 实现的 parameterize 方法中需要对参数进行预处理,进行参数化时需要用到

this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);

// <3> 创建 DefaultResultSetHandler 对象

this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);

}

}

关于它的属性可以根据注释进行理解

1. 如果入参中的boundSql为null,则需要进行初始化,可以会看到SimpleExecutor中执行数据库的更新操作时,传入的boundSql为null,数据库的查询操作才会传入该对象的值

1. 调用generateKeys(Object parameter)方法,根据配置的KeyGenerator对象,在SQL执行之前执行查询操作获取值,设置到入参对象对应属性中,代码如下:

2. protected void generateKeys(Object parameter) {

3. /*

4. * 获得 KeyGenerator 对象

5. * 1. 配置了 <selectKey /> 则会生成 SelectKeyGenerator 对象

6. * 2. 配置了 useGeneratedKeys="true" 则会生成 Jdbc3KeyGenerator 对象

7. * 否则为 NoKeyGenerator 对象

8. */

9. KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();

10. ErrorContext.instance().store();

11. // 前置处理,创建自增编号到 parameter 中

12. keyGenerator.processBefore(executor, mappedStatement, null, parameter);

13. ErrorContext.instance().recall();

14. }

只有配置的<selectKey />标签才有前置处理,这就是为什么数据库的更新操作传入的boundSql为null的原因,因为入参中有的属性值可能需要提前生成一个值(执行配置的SQL语句),KeyGenerator会在后续讲到

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值