17-Mybatis源码分析(StatementHandler数据库访问)

StatementHandler

一、StatementHandler和Statement

1.1 StatementHandler接口

  • StatementHandler是Mybatis四大对象之一,它完成Mybatis最核心的工作,它封装了Statement的相关操作来完成数据库的访问,也是Executor组件的基础。包括创建Statement对象,为sql绑定参数,执行sql,结果集映射等。它是一个接口,使用不同的实现类来处理不同的情况,并且采用了模板模式,使用BaseStatementHandler来完成基本骨架,子类继承然后实现具体方法细节。
  • 在了解StatementHandler之前,我们先了解一下Statement。

1.2 Statement接口

  • Statement是JDK的java.sql包下的接口,JDK源码注释表示Statement是用于执行sql语句并返回结果集的对象,接口定义了很多关于数据访问和处理的方法。

//The object used for executing a static SQL statement and returning the results it produces

public interface Statement extends Wrapper, AutoCloseable {

    //执行sql,返回单个结果集
    ResultSet executeQuery(String sql) throws SQLException;
    
    //执行增、删或者改的sql语句
    int executeUpdate(String sql) throws SQLException;
    
    //关闭Statement
    void close() throws SQLException;
    
    //执行sql,可返回多个结果集
    boolean execute(String sql) throws SQLException;
    
    //其他方法...省略...
}
  • Statement接口的主要实现类是PreparedStatement,另外还有CallableStatemen和其他实现类。

  • 我们简单了解Statement的功能即可,知道Statement在访问数据库过程中的作用,后面的StatementHandler实现的相关功能则是围绕Statement的。

二、StatementHandler

2.1 接口

  • StatementHandler接口封装了关于Statement的很多操作方法,比如获取Statement的prepare方法,处理参数的parameterize方法(处理参数内部调用的还是Statement自身的方法)、查询或者更新方法query和update等。简而言之StatementHandler的存在让很多原本需要访问Statement的操作现在只需要访问StatementHandler,不过本质上还是访问Statement,只不过做了一些封装和简化。
public interface StatementHandler {

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

  void parameterize(Statement statement)
      throws SQLException;

  void batch(Statement statement)
      throws SQLException;

  int update(Statement statement)
      throws SQLException;

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

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

  BoundSql getBoundSql();

  ParameterHandler getParameterHandler();

}

2.2 实现类

  • StatementHandler子类如下:

在这里插入图片描述

在这里插入图片描述

实现类描述
BaseStatementHandler不同子类的抽象父类,模板模式的骨架方法实现,同时也实现了大部分接口方法,简化子类的实现
RoutingStatementHandler静态代理,本身没有额外功能,Execuotr中实例化的就是它,它会根据具体的环境实例化不同的子类
SimpleStatementHandler使用statement访问数据库,无需参数化
PreparedStatementHandler使用预编译的PrepareStatement访问数据库
CallableStatementHandler存储过程处理
  • StatementHandler中使用了模板模式。举一个模板模式体现的例子:在StatementHandler中定义了prepare方法获取Statement,在BaseStatementHandler中完成了prepare方法的主体流程,但是真正创建Statement实例在BaseStatementHandler没有实现,而是通过一个abstract的方法instantiateStatement交给子类去实现,因为不同的三个子类创建Statement的逻辑是不一样的,这就是模板模式的体现。另外在BaseStatementHandler中也实现了StatementHandler中的很多方法,这样三个真正的子类只需要实现部分方法即可,大部分方法都在BaseStatementHandler中实现了,通过继承获取,简化了部分代码。

三、实现类分析

3.1 BaseStatementHandler

  • BaseStatementHandler是StatementHandler的实现类,也是其他几个实现类的类抽象父类,实现类主要的一些方法,其中包括获取Statement的prepare方法的主流程,但是把实例化Statement的步骤通过抽象方法instantiateStatement交给不同的子类实现。
/**
   * StatementHandler创建Statement的主要逻辑都在这里,StatementHandler的其他子类并没有实现这个方法,直接从BaseStatementHandler
   * 继承获得到,但是Statement的真正实例化是在抽象方法instantiateStatement中,留给不同的子类实现的
   * */
  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      //创建Statement主流程
      statement = instantiateStatement(connection);
      setStatementTimeout(statement, transactionTimeout);
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }
  
  
  protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

3.2 RoutingStatementHandler

  • RoutingStatementHandler没有实现额外功能,它通过静态代理模式起到了一个路由的功能,根据不同的场景选择合适的StatementHandler。它的内部持有一个StatementHandler对象delegate,在构造方法总实例化内部的delegate,然后实现了StatementHandler的全部方法,实际上都是调用delegate来执行,自己就像一个路由角色,代码如下:
public class RoutingStatementHandler implements StatementHandler {

  private final StatementHandler delegate;

 //构造方法,在构造方法总实例化内部的delegate,然后实现StatementHandler的所有方法,实际上都是调用delegate来执行,自己就像一个路由角色
  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

    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());
    }
  }
    
  //然后继承自StatementHandler的方法实际上都是调用delegate来执行
  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    return delegate.prepare(connection, transactionTimeout);
  }

   //省略其他....
}
  • 在映射文件的sql语句中可以设置StatementType,在RoutingStatementHandler里面到底是要哪一个,取决于select里面配置的是哪一种。MappedStatement是sql语句映射的数据结构,在MappedStatement中保存了StatementType,RoutingStatementHandler实例化真实的子类时会根据MappedStatement.getStatementType()进行判断。
public enum StatementType {
  STATEMENT, PREPARED, CALLABLE
}

在这里插入图片描述

3.3 PreparedStatementHandler

  • StatementType默认是PREPARED,下面通过一次查询的调试步骤看看获取Statement的过程。

  • 第一步:SimpleExecutor的doQuery方法中:configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);创建StatementHandler创建的就是RoutingStatementHandler。

在这里插入图片描述

  • 第二步:在SimpleExecutor#prepareStatement方法中,调用RoutingStatementHandler的prepare方法。

在这里插入图片描述

  • 第三步:在RoutingStatementHandler的prepare方法中,调用的是PreparedStatementHandler的prepare方法。(默认情况下使用的就是PreparedStatementHandler,此时RoutingStatementHandler内部的代理是PreparedStatementHandler

在这里插入图片描述

  • 第四步:走到抽象父类BaseStatementHandler的prepare方法,这是创建Statement的骨架方法,但是instantiateStatement实例化的过程会走到子类PreparedStatementHandler

在这里插入图片描述

  • 第五步:走到PreparedStatementHandler的instantiateStatement方法实例化Statement成功。

在这里插入图片描述

  • 第六步:最后一步一步返回到SimpleExecutor类的doQuery方法进行数据库后续的操作。流程图可以参考4.1小节

3.4 SimpleStatementHandler

  • SimpleStatementHandler和PreparedStatementHandler相比最大的区别是前者不会对sql语句进行预编译,因此在实例化Statement的核心方法instantiateStatement和PreparedStatementHandler有所不同
/**
   *
   * SimpleStatementHandler不会预编译sql语句,返回Statement
   * */
  @Override
  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();
    }
  }

3.5 CallableStatementHandler

  • CallableStatementHandler主要处理存储过程,不做重点分析。

四、小结

4.1 流程图

  • 对于3.3小结的调试过程,下面使用流程图展示过程

在这里插入图片描述

4.2 小结

  • StatementHandler对Statement的相关操作进行了封装,他有有一个抽象子类BaseStatementHandler实现了大部分方法和主要方法的核心流程,将不同情况下Statement的实例化步骤通过抽象方法交给不同的子类实现
  • BaseStatementHandler是三种不同子类SimpleStatementHandler、PreparedStatementHandler和CallableStatementHandler的抽象父类,PreparedStatementHandler是默认的情况,会预编译sql语句,SimpleStatementHandler则不会
  • StatementType有3中切换模式,在Mybatis的映射文件的sql节点可以配置
  • 不管是哪一种Statement,他都是被Executor组件使用,其实Executor实例化的是RoutingStatementHandler,RoutingStatementHandler内部实例化不同的子类对象。Executor组件是执行sql操作的组件,但是实际上Executor还是通过StatementHandler来操作Statement,自上而下来看,本质上还是JDBC的访问。

五、参考

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值