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