MyBatis學習筆記(二) Executor


一、概述

當我們打開一個SqlSession的時候,我們就完成了操作數據庫的第一步,那MyBatis是如何執行Sql的呢?其實MyBatis的增刪改查都是通過Executor執行的,Executor和SqlSession綁定在一起,由Configuration類的newExecutor方法創建。

 

二、Executor類圖

首先,頂層接口是Executor,有兩個實現類,分別是BaseExecutor和CachingExecutor,CachingExecutor用於二級緩存,而BaseExecutor則用於一級緩存及基礎的操作,BaseExecutor是一個抽象類,又有三個實現,分別是SimpleExecutor,BatchExecutor,ReuseExecutor,而具體使用哪一個Executor則是可以在mybatis-config.xml中進行配置的,配置方式如下:

<settings>
    <!--SIMPLE、REUSE、BATCH-->
    <setting name="defaultExecutorType" value="REUSE"/>
</settings>

如果沒有配置Executor,默認情況下是SimpleExecutor。

 

三、各個Executor介紹

1.BaseExecutor

相當於一個基礎,實現了Executor的方法,但是只是做一些准備工作,比如查詢的CacheKey定義等,以及公共方法的定義,比如close、commit、rollback方法等,而具體的執行則是定義了抽象方法doUpdate、doQuery,這些將由BaseExecutor的子類實現,如下

BaseExecutor這里采用了模板方法模式

  protected abstract int doUpdate(MappedStatement ms, Object parameter)
      throws SQLException;

  protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
      throws SQLException;

  protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
      throws SQLException;

  protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
      throws SQLException;

 

1.1 SimpleExecutor

最簡單的執行器,根據對應的sql直接執行,每執行一次update或select,就開啟一個Statement對象,用完立刻關閉Statement對象。(可以是Statement或PrepareStatement對象)

 

1.2 BatchExecutor

執行update(沒有select,JDBC批處理不支持select),將所有sql都添加到批處理中(addBatch()),等待統一執行(executeBatch()),它緩存了多個Statement對象,每個Statement對象都是addBatch()完畢后,等待逐一執行executeBatch()批處理的;BatchExecutor相當於維護了多個桶,每個桶里都裝了很多屬於自己的SQL,就像蘋果藍里裝了很多蘋果,番茄藍里裝了很多番茄,最后,再統一倒進倉庫。(可以是Statement或PrepareStatement對象)

通常需要注意的是批量更新操作,由於內部有緩存的實現,使用完成后記得調用flushStatements來清除緩存。

  @Override
  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);
      applyTransactionTimeout(stmt);
     handler.parameterize(stmt);//fix Issues 322
      BatchResult batchResult = batchResultList.get(last);
      batchResult.addParameterObject(parameterObject);
    } else {
      Connection connection = getConnection(ms.getStatementLog());
      stmt = handler.prepare(connection, transaction.getTimeout());
      handler.parameterize(stmt);    //fix Issues 322
      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;
  }

 

1.3 ReuseExecutor

可重用的執行器,重用的對象是Statement,也就是說該執行器會緩存同一個sql的Statement,省去Statement的重新創建,優化性能。內部的實現是通過一個HashMap來維護Statement對象的。由於當前Map只在該session中有效,所以使用完成后記得調用 flushStatements來清除Map。
private final Map<String, Statement> statementMap = new HashMap<String, Statement>();

 

2.CachingExecutor

先從緩存中獲取查詢結果,存在就返回,不存在,再委托給Executor delegate去數據庫取,delegate可以是上面任一的SimpleExecutor、ReuseExecutor、BatchExecutor。

這幾個Executor的生命周期都是局限於SqlSession范圍內。

  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this);
  }

......

  @Override
  public int update(MappedStatement ms, Object parameterObject) throws SQLException {
    flushCacheIfRequired(ms);
    return delegate.update(ms, parameterObject);
  }

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

  @Override
  public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
    flushCacheIfRequired(ms);
    return delegate.queryCursor(ms, parameter, rowBounds);
  }

  @Override
  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);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

......

 

四、Executor初始化

1.先調用startManagedSession

  public void startManagedSession() {
    this.localSqlSession.set(openSession());
  }

2.調用startManagedSession方法來使SqlSessionManager內部維護的localSqlSession變量生效,這一步操作中會涉及到對Executor的獲取,代碼如下:

 @Override
  public SqlSession openSession() {
    return sqlSessionFactory.openSession();
  }


 @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //這里根據不同的executortype獲取不同的Executor 
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

3.最后就是文章開頭所說的newExecutor方法創建返回不同的Executor 

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

 

參考文章:

https://www.jianshu.com/p/53cc886067b1

 

https://blog.csdn.net/cleargreen/article/details/80614362

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM