一、概述
當我們打開一個SqlSession的時候,我們就完成了操作數據庫的第一步,那MyBatis是如何執行Sql的呢?其實MyBatis的增刪改查都是通過Executor執行的,Executor和SqlSession綁定在一起,由Configuration類的newExecutor方法創建。
1、頂層接口是Executor,有兩個實現類,分別是BaseExecutor和CachingExecutor,CachingExecutor用於二級緩存,而BaseExecutor則用於一級緩存及基礎的操作,BaseExecutor是一個抽象類,又有三個實現,分別是SimpleExecutor,BatchExecutor,ReuseExecutor,而具體使用哪一個Executor則是可以在mybatis-config.xml中進行配置的,配置方式如下:
<settings>
<!--SIMPLE、REUSE、BATCH-->
<setting name="defaultExecutorType" value="REUSE"/>
</settings>
二、各個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。
2.CachingExecutor
先從緩存中獲取查詢結果,存在就返回,不存在,再委託給Executor delegate去數據庫取,delegate可以是上面任一的SimpleExecutor、ReuseExecutor、BatchExecutor。
這幾個Executor的生命週期都是侷限於SqlSession範圍內。
三、示例
1、batch模式
public void batchUpdate(String str, List<?> objs )throws Exception{
SqlSessionFactory sqlSessionFactory = sqlSessionTemplate.getSqlSessionFactory();
//批量執行器
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
try{
if(objs!=null){
for(int i=0,size=objs.size();i<size;i++){
sqlSession.update(str, objs.get(i));
}
sqlSession.flushStatements();
sqlSession.commit();
sqlSession.clearCache();
}
}finally{
sqlSession.close();
}
}
一次添加三條數據,一下是打印記錄,由此可看出一次執行插入三條數據