MyBatis學習筆記(Executor)

一、概述

當我們打開一個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();
		}
	}

一次添加三條數據,一下是打印記錄,由此可看出一次執行插入三條數據

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章