MyBatis Learning Notes Executor

Keywords: Java SQL Mybatis Database Session

I. overview

When we open a SqlSession, we complete the first step of operating the database. How does MyBatis execute Sql? In fact, MyBatis additions and deletions are checked through Executor, which is bound to SqlSession and created by the new Executor method of the Configuration class.

 

Executor class diagram

First, the top-level interface is Executor. There are two implementation classes: BaseExecutor and ChaingExecutor, CachingExecutor for secondary cache, and BaseExecutor for primary cache and basic operation. BaseExecutor is an abstract class and has three implementations, namely SimpleExecutor, BatchExecutor, ReuseExecutor. Which Executor can be used specifically? Configured in mybatis-config.xml, the configuration is as follows:

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

If Executor is not configured, it is SimpleExecutor by default.

 

Introduction of Executor s

1.BaseExecutor

Equivalent to a foundation, implements Executor's method, but only does some preparatory work, such as the definition of CacheKey for queries, and the definition of common methods, such as close, commit, rollback methods, etc., while the specific implementation is to define abstract methods doUpdate, doQuery, which will be implemented by the subclasses of BaseExecutor, as follows:

BaseExecutor uses the template method pattern here

  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

The simplest executor, which executes directly according to the corresponding sql, opens a Statement object every time an update or select is executed, and closes the Statement object immediately after use. (Can be a Statement or PrepareStatement object)

 

1.2 BatchExecutor

Perform update (no selection, JDBC batch does not support selection), add all SQL to the batch (addBatch (), wait for unified execution (executeBatch ()), it caches several Statement objects, each Statement object is addBatch () after the completion of the execution of executeBatch () by batch; Batch Executor is equivalent to maintaining several buckets Each bucket contains a lot of its own SQL, just like Apple Blue contains a lot of apples, tomato blue contains a lot of tomatoes, and finally, unified into the warehouse. (Can be a Statement or Prepare Statement object)

Often it is important to note that the batch update operation, because of the internal cache implementation, remember to call flushStatements to clear the cache after use.

  @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

The reusable executor, the reusable object is Statement, that is to say, the executor caches the Statement of the same sql, eliminating the re-creation of Statement and optimizing performance. The internal implementation maintains the Statement object through a HashMap. Since the current Map is only valid in that session, remember to call flushStatements to clear the Map when it's finished.
private final Map<String, Statement> statementMap = new HashMap<String, Statement>();

 

2.CachingExecutor

The query results are retrieved from the cache, returned when they exist, and then delegated to Executor delegate to the database. Delegate can be either SimpleExecutor, Reuse Executor, Batch Executor.

The life cycles of these Executor s are limited to 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);
  }

......

 

IV. Executor initialization

1. Call startManagedSession first

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

2. Call the startManagedSession method to make the localSqlSession variable maintained within SqlSession Manager valid. This step involves obtaining Executor. The code is as follows:

 @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);
      //It's different here. executortype Getting different 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. Finally, the new Executor method created at the beginning of the article returns different Executors.

  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;
  }

 

Reference article:

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

 

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

Posted by dc519 on Tue, 07 May 2019 02:40:39 -0700