一、概述 當我們打開一個SqlSession的時候,我們就完成了操作資料庫的第一步,那MyBatis是如何執行Sql的呢?其實MyBatis的增刪改查都是通過Executor執行的,Executor和SqlSession綁定在一起,由Configuration類的newExecutor方法創建。 二 ...
一、概述
當我們打開一個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