Mybatis解析-执行器Executor详解

本文基于mybatis-spring 1.3.1和mybatis 3.4.4版本

SqlSession执行增删改查都是委托给Executor完成的。
Executor主要完成以下几项内容:

  1. 处理缓存,包括一级缓存和二级缓存
  2. 获取数据库连接
  3. 创建Statement或者PrepareStatement对象
  4. 访问数据库执行SQL语句
  5. 处理数据库返回结果

Executor继承结构如下:
在这里插入图片描述
接下来本文详细介绍各个实现类。

一、CachingExecutor

CachingExecutor用于处理二级缓存,如果缓存中不存在要查询的数据,那么将查询请求委托给其他的Executor。如果是执行SQL的增删改,那么CachingExecutor将清空二级缓存。
关于CachingExecutor的其他内容可以参见《Mybatis解析-缓存原理解析》。

二、BaseExecutor

BaseExecutor是除CachingExecutor之外,其他Executor实现类的基类。该类主要处理一级缓存。该类中属性localCache表示一级缓存。

protected PerpetualCache localCache;

当调用该类的查询方法时,先查看一级缓存中是否已经有数据,如果有则直接从缓存获取,如果没有调用子类的查询方法从数据库中获取。
当调用该类的update方法(mybatis将delete和insert认为是update,统一调用该类update方法)时,BaseExecutor将一级缓存清空,然后调用子类对应的增删改方法。
调用执行rollback/commit方法时,该类清空一级缓存。

三、SimpleExecutor

SimpleExecutor继承自BaseExecutor,该类比较简单。
当执行增删改查时,该类获取数据库连接,创建PrepareStatement或者Statement对象,执行SQL语句,最后将数据库返回结果转化为设定的对象。下面以doQuery方法为例:

  //父类执行查询时调用该方法
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
       //创建StatementHandler对象
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //获得数据库连接,创建Statement或者PrepareStatement
      stmt = prepareStatement(handler, ms.getStatementLog());
      //执行SQL语句,将数据库返回结果转化为设定的对象,比如List,Map或者是POJO
      return handler.<E>query(stmt, resultHandler);
    } finally {
      //关闭Statement对象
      closeStatement(stmt);
    }
  }

SimpleExecutor基本是按照标注JDBC流程执行SQL语句获得返回结果。

四、BatchExecutor

当执行查询时,BatchExecutor与SimpleExecutor的处理逻辑是一样的。不同的是执行更新方法(mybatis认为delete和insert都是update)。执行更新方法时,BatchExecutor不是直接执行SQL语句,而是将其放到批次里面,等到提交的时候一起执行。

  public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
    final Configuration configuration = ms.getConfiguration();
    //创建Statement或者PreparedStatement对象
    final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
    final BoundSql boundSql = handler.getBoundSql();
    //获得原始SQL语句
    final String sql = boundSql.getSql();
    final Statement stmt;
    //判断当前执行的SQL是否在上一次已经执行过
    //下面的条件表示,SQL语句相同,调用的mapper接口方法也相同
    if (sql.equals(currentSql) && ms.equals(currentStatement)) {
      //如果在上一次已经执行过,那么直接复用上一次使用的Statement或者PreparedStatement对象
      int last = statementList.size() - 1;
      stmt = statementList.get(last);
      applyTransactionTimeout(stmt);
      handler.parameterize(stmt);
      //batchResult用于记录批次执行结果
      BatchResult batchResult = batchResultList.get(last);
      batchResult.addParameterObject(parameterObject);
    } else {
      //下面的内容是新建Statement或者PreparedStatement对象,用于执行新的SQL语句
      Connection connection = getConnection(ms.getStatementLog());
      stmt = handler.prepare(connection, transaction.getTimeout());
      handler.parameterize(stmt);  
      currentSql = sql;
      currentStatement = ms;
      statementList.add(stmt);
      batchResultList.add(new BatchResult(ms, sql, parameterObject));
    }
    //将SQL加入批次
    handler.batch(stmt);
    //返回一个常量值,表示当前是批次执行
    return BATCH_UPDATE_RETURN_VALUE;
  }

当连续执行的SQL语句相同时,BatchExecutor才会将其加入到同一个批次中,否则新建Statement或者PreparedStatement对象,并创建新批次。
当提交事务时,会执行BatchExecutor的doFlushStatements方法,

  public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
    try {
      List<BatchResult> results = new ArrayList<>();
      if (isRollback) {
        return Collections.emptyList();
      }
      //遍历每个批次
      for (int i = 0, n = statementList.size(); i < n; i++) {
        Statement stmt = statementList.get(i);
        applyTransactionTimeout(stmt);
        BatchResult batchResult = batchResultList.get(i);
        try {
          //执行每个批次,将批次执行结果存入BatchResult对象
          batchResult.setUpdateCounts(stmt.executeBatch());
          //删减代码
          closeStatement(stmt);
        } catch (BatchUpdateException e) {
      //代码删减
      return results;
    } finally {
      for (Statement stmt : statementList) {
        //关闭每个Statement对象
        closeStatement(stmt);
      }
      currentSql = null;
      statementList.clear();
      batchResultList.clear();
    }
  }

五、ReuseExecutor

ReuseExecutor从名字可以看出该类主要特点是复用。它复用的是Statement对象或者PreparedStatement对象。
该类中有一个属性statementMap,如下面代码所示,key是SQL语句,value是Statement对象。每次执行首先根据SQL语句查询statementMap,如果有对应的Statement对象,则直接使用该Statement对象,如果没有,则创建新的Statement对象,然后将其和SQL语句添加到statementMap,以备下次使用。

private final Map<String, Statement> statementMap = new HashMap<>();

下面来一下具体代码实现。

  //增删改操作
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    //创建StatementHandler对象
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
    //创建或者复用Statement对象
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.update(stmt);
  }
  //查询
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    //创建StatementHandler对象
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    //创建或者复用Statement对象
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.query(stmt, resultHandler);
  }
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    BoundSql boundSql = handler.getBoundSql();
    //得到原始SQL语句
    String sql = boundSql.getSql();
    //检查该SQL是否在statementMap中存在
    if (hasStatementFor(sql)) {
      //得到已经存在的Statement对象
      stmt = getStatement(sql);
      //设置超时参数
      applyTransactionTimeout(stmt);
    } else {
      //如果之前没有执行过该SQL,那么获取连接
      Connection connection = getConnection(statementLog);
      //创建新的Statement对象
      stmt = handler.prepare(connection, transaction.getTimeout());
      //将SQL语句和Statement对象添加到statementMap中,以备后面复用
      putStatement(sql, stmt);
    }
    //如果stmt是PreparedStatement对象,下面的方法用于设置SQL语句的参数
    handler.parameterize(stmt);
    return stmt;
  }

六、ClosedExecutor

ClosedExecutor是ResultLoaderMap内部类,外部无法使用,调用其方法会抛出异常。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值