讀mybatis源碼之七:執行器Exceutor之四大執行器

一、SimpleExecutor

SimpleExecutor繼承BaseExecutor
主要實現:doUpdate、doQuery、doFlushStatements
可以看代碼主要是:
1、獲取聲明處理類:StatementHandler handler
2、獲取聲明類:stmt = prepareStatement(handler, ms.getStatementLog());
3、處理聲明,執行SQL處理

二、ReuseExecutor

與SimpleExecutor不同的是:重用聲明
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    BoundSql boundSql = handler.getBoundSql();
    String sql = boundSql.getSql();
    if (hasStatementFor(sql)) {
      stmt = getStatement(sql);
    } else {
      Connection connection = getConnection(statementLog);
      stmt = handler.prepare(connection);
      putStatement(sql, stmt);
    }
    handler.parameterize(stmt);
    return stmt;
  }
SQL語句一樣的,就用同樣的聲明。不過這個好像也僅僅是同一個session內的有效。

三、BatchExecutor

先看update
 public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
    final Configuration configuration = ms.getConfiguration();
    final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
    final BoundSql boundSql = handler.getBoundSql();
    final String sql = boundSql.getSql();
    final Statement stmt;
    if (sql.equals(currentSql) && ms.equals(currentStatement)) {
      int last = statementList.size() - 1;
      stmt = statementList.get(last);
      BatchResult batchResult = batchResultList.get(last);
      batchResult.addParameterObject(parameterObject);
    } else {
      Connection connection = getConnection(ms.getStatementLog());
      stmt = handler.prepare(connection);
      currentSql = sql;
      currentStatement = ms;
      statementList.add(stmt);
      batchResultList.add(new BatchResult(ms, sql, parameterObject));
    }
    handler.parameterize(stmt);
    handler.batch(stmt);
    return BATCH_UPDATE_RETURN_VALUE;
  }
if (sql.equals(currentSql) && ms.equals(currentStatement))
表明同一個SQL語句使用同一個申明,應用場景很簡單,就是我們的批量刪除和批量插入,在沒有參數的SQL語句他們就是一樣的,不一樣的只是參數而已。具體參數處理在StatementHandler裏面去設置的。也是同一個session內的批量操作。是不是很奇怪沒有提交?看完查詢再說:
public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
      throws SQLException {
    Statement stmt = null;
    try {
      flushStatements();
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, rowBounds, resultHandler, boundSql);
      Connection connection = getConnection(ms.getStatementLog());
      stmt = handler.prepare(connection);
      handler.parameterize(stmt);
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
重點flushStatements,爲什麼這裏會做這件事呢?因爲在同一session裏面如果又是批量操作,又是查詢的話,那麼就必須保證批量操作數據提交上去了。也就是爲什麼有這個,這個方法主要的事情歸結到doFlushStatements,就是批量更新提交。查詢有提交,批量更新那個怎麼沒有提交?在sqlsession裏面commit調用BaseExecutor裏面commit
public void commit(boolean required) throws SQLException {
    if (closed) throw new ExecutorException("Cannot commit, transaction is already closed");
    clearLocalCache();
    flushStatements();
    if (required) {
      transaction.commit();
    }
  }
flushStatements();
注意使用批量的時候,如果不開啓批量模式,是沒有使用批量更新的,使用sessionFactory.openSession(ExecutorType.BATCH, false);
網上大部分例子都沒有正確使用批量更新,因爲mybatis默認是SimpleExecutor執行器

四、CachingExecutor

緩存執行器,顧名思義跟緩存有關係,這裏的緩存是全局緩存,configuration裏面的cacheEnabled參數決定,默認爲true
也就是默認是開啓全局緩存,但是隻是這裏開啓就可以嗎?詳細看看CachingExecutor這個類:
先看創建這個類:
  public CachingExecutor(Executor delegate) {
    this(delegate, false);
  }
  public CachingExecutor(Executor delegate, boolean autoCommit) {
    this.delegate = delegate;
    this.autoCommit = autoCommit;
  }
裝飾模式,也就是說緩存執行器,增強了其他的執行器,不同的是他先執行緩存處理。(以後需要加緩存時可以借鑑)
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
    flushCacheIfRequired(ms);
    return delegate.update(ms, parameterObject);
  }
更新先flush緩存
 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    Cache cache = ms.getCache();
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, parameterObject, boundSql);
        if (!dirty) {
          cache.getReadWriteLock().readLock().lock();
          try {
            @SuppressWarnings("unchecked")
            List<E> cachedList = (List<E>) cache.getObject(key);
            if (cachedList != null) return cachedList;
          } finally {
            cache.getReadWriteLock().readLock().unlock();
          }
        }
        List<E> list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks
        return list;
      }
    }
    return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

查詢重點:
Cache cache = ms.getCache();  看mapper上配置緩存沒有,如果有執行緩存邏輯,所以默認全局緩存開啓後,每個mapper上必須配置是否使用緩存,緩存纔有效。這個緩存在MappedStatement上,所以是全局的,非僅僅在session內有效。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章