MyBatis源码学习—MyBatis数据仓库会话器StatementHandler详解

1、StatementHandler对象的创建过程

在上一节MyBatis源码学习—MyBatis 执行器Executor详解中我们了解MyBatis执行器的产生以及执行SQL的大致过程。StatementHandler 对象是在 SqlSession 对象接收到SQL执行命令时,由 Configuration 对象中的newStatementHandler 负责调用的,也就是说 Configuration 中的 newStatementHandler 是由执行器中的查询、更新(插入、更新、删除)方法来提供的,StatementHandler 其实就是由 Executor 负责管理和创建的。

以查询为例,在SimpleExecutor中最终落到了doQuery()这个方法,如下图所示。

在执行查询逻辑中比较重要的异步操作就是StatementHandler handler = configuration.newStatementHandler(......)这一句代码,从字面意思看,它就是创建了一个StatementHandler实例对象,一个用来执行SQL查询的处理器。

追进去看看newStatementHandler方法干了什么,如下图所示Configuration类的newStatementHandler方法。

可以看到他直接为我们new了一个RoutingStatementHandler对象,然后返回了这个对象。RoutingStatementHandler在下面会详细介绍,别着急!!!

2、StatementHandler继承结构

和Executor体系的继承体系类似,最顶层的接口是StatementHandler,往下有2个直接实现类:BaseStatementHandlerRoutingStatementHandler,BaseStatementHandler又有3个子类:PreparedStatementHandlerSimpleStatementHandler以及CallableStatementHandler

StatementHandler:MyBatis四大组件中最重要的一个对象,负责操作 Statement 对象(JDBC)与数据库进行交流,在工作时还会使用 ParameterHandler 和 ResultSetHandler 对参数进行映射,对结果进行实体类的绑定。

StatementHandler其实就是对JDBC中Statement的包装,为了一探究竟我们直接打开MyBatis源码来看一下。下图所示是StatementHandler接口的方法。

  • prepare():用于创建一个具体的Statement对象的实现类实例
  • parameterize():用于初始化Statement实例对象以及对SQL语句中的占位符进行数据填充
  • update():用于通知Statement对象将insert、update、delete操作发送给数据库
  • query():用于通知Statement对象将select操作发送到数据库

RoutingStatementHandler:从它的名字就可以窥见它的作用,它的作用就是选择合适的StatementHandler实现类的,具体就是根据StatementType 参数来创建一个代理,代理的就是对应Handler的三种实现类

看到这里,是不是对RoutingStatementHandler这个类的作用恍然大悟~~他就类似 与一个路由器,把一条SQL请求路由到一个具体的StatementHandler实现类去

BaseStatementHandler:是StatementHandler接口的另一个实现类,本身是一个抽象类,用于简化StatementHandler 接口实现的难度,属于适配器设计模式体现,它主要有三个实现类:

  • SimpleStatementHandler:用于处理不需要预编译的SQL语句,类似于JDBC中普通的Statement
  • PraparedStatementHandler:用于处理不需要预编译的SQL语句,类似于JDBC中的PraparedStatement
  • CallableStatementHandler:用于处理存储过程,类似JDBC中的CallableStatement

3、StatementHandler核心方法源码分析

prepare方法调用流程分析

执行器 Executor 在执行SQL语句的时候会创建 StatementHandler 对象,进而经过一系列的 StatementHandler 类型的判断并初始化。再拿到StatementHandler 返回的 statementhandler 对象的时候,会调用其prepareStatement()方法,下面就来一起看一下 preparedStatement() 方法

从Executor的doQuery方法开始,首先实例化一个Statement对象,其实就是RoutingStatementHandler,在RoutingStatementHandler内部维护了一个真实干活的StatementHandler对象。实例化完成后,调用了执行器的prepareStatement方法获取数据库连接Connetion,紧接着调用了StatementHandler的prepare方法,这个调用首先会到RoutingStatementHandler中的同名方法,之后会调到BaseStatementHandler中,这个方法只在BaseStatementHandler有具体实现,在这个实现中,它又调用了**instantiateStatement **,这个方法在BaseStatementHandler没有具体实现,实际执行的是三种 StatementHandler 中的一种,我们还以 SimpleStatementHandler 为例

protected Statement instantiateStatement(Connection connection) throws SQLException {
    if (mappedStatement.getResultSetType() != null) {
      return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
      return connection.createStatement();
    }
  }

从上面代码我们可以看到,instantiateStatement() 最终返回的是Statement对象,经过一系列的调用会把Statement 对象返回到 SimpleExecutor 执行器中,为 parametersize 方法所用。也就是说,prepare 方法负责生成 Statement 实例对象,而 parameterize 方法用于处理 Statement 实例对应的参数

parametersize 方法调用流程分析

在上面的prepareStatement方法中执行器拿到具体的Statement对象实例之后就会调用parametersize方法用处理Statement实例的参数

对于这个SimpleStatementHanlder在前面我们说过他就是用来处理简单SQL语句的,因此它的实现方法是空的,如下图所示。

因此具体的原理我们着重看PraparedStatementHandler的同名方法的实现,如下图所示。

可以看到,具体给SQL语句参数赋值的操作MyBatis有把它交给了下一个组件ParameterHandler(参数处理器),具体内容请参考我的下一篇博文:MyBatis源码学习—MyBatis参数处理器ParameterHandler详解

query 方法调用流程分析

StatementHandler中query方法是用于执行SQL查询语句的方法,它的具体实现在BaseStatementHandler中没有实现,我们以SimpleStatementHandler为例来分析一下。下图所示是SimpleStatementHandler的query方法。

流程非常简单暴力:获取到sql语句=>调用Statement的execute方法执行sql=>调用结果处理器封装查询结果。具体查询结果集封装返回值的类容请参考我的博文:MyBatis源码学习—MyBatis结果处理器ResultHandler详解

update 方法调用流程分析

StatementHandler的update方法是用于SQL语句的insert、update、delete语句的执行,我们以SimpleStatementHandler为例来分析一下。下图所示是SimpleStatementHandler的update方法。

简单描述一下update 方法的执行过程:

  1. MyBatis 接收到 update 请求后会先找到 CachingExecutor 缓存执行器查询是否需要刷新缓存,然后找到BaseExecutor 执行 update 方法;
  2. BaseExecutor 基础执行器会清空一级缓存,然后交给再根据执行器的类型找到对应的执行器,继续执行 update 方法;
  3. 具体的执行器会先创建 Configuration 对象,根据 Configuration 对象调用 newStatementHandler 方法,返回 statementHandler 的句柄;
  4. 具体的执行器会调用 prepareStatement 方法,找到本类的 prepareStatement 方法后,再有prepareStatement 方法调用 StatementHandler 的子类 BaseStatementHandler 中的 prepare 方法
  5. BaseStatementHandler 中的 prepare 方法会调用 instantiateStatement 实例化具体的 Statement 对象并返回给具体的执行器对象
  6. 由具体的执行器对象调用 parameterize 方法给参数进行赋值。

用一幅图来表示一下这个调用过程:

参考资料

留言区

还能输入500个字符