Mybatis源碼學習第八課---核心層源碼分析--接口層Executor

  Executor 是MyBatis的核心接口之一,其中定義了數據庫操作的基本方法。在實際應用中經常設計的SqlSession接口的功能,都是基於Executor接口實現的。

    SQL 語句的執行涉及多個組件,包括 MyBatis 的四大核心,它們是: ExecutorStatementHandlerParameterHandlerResultSetHandler。SQL 的執行過程可以用下面這幅圖來表示。

        

MyBatis 層級結構各個組件的介紹:

  • SqlSession: ,它是 MyBatis 核心 API,主要用來執行命令,獲取映射,管理事務。接收開發人員提供 Statement Id 和參數。並返回操作結果。
  • Executor :執行器,是 MyBatis 調度的核心,負責 SQL 語句的生成以及查詢緩存的維護。
  • StatementHandler : 封裝了JDBC Statement 操作,負責對 JDBC Statement 的操作,如設置參數、將Statement 結果集轉換成 List 集合。
  • ParameterHandler : 負責對用戶傳遞的參數轉換成 JDBC Statement 所需要的參數。
  • ResultSetHandler : 負責將 JDBC 返回的 ResultSet 結果集對象轉換成 List 類型的集合。
  • TypeHandler : 用於 Java 類型和 JDBC 類型之間的轉換。
  • MappedStatement : 動態 SQL 的封裝
  • SqlSource : 表示從 XML 文件或註釋讀取的映射語句的內容,它創建將從用戶接收的輸入參數傳遞給數據庫的 SQL。
  • Configuration: MyBatis 所有的配置信息都維持在 Configuration 對象之中。
     

一、Executor 說明

    MyBatis 提供 Executor 接口實現如下圖。

                         

         在這些 Executor接口實現中涉及兩種設計模式:模板模式和裝飾器模式

         裝飾器模式:二級緩存 CachingExecutor扮演了裝飾器的角色,爲Executor添加二級緩存功能。 

         模板模式:BaseExecutor是實現Executor接口的抽象類,其中主要提供了緩存管理和事務管理的基本功能。而其繼承其子類只有實現四個方法分別 是: doUpdate()方法、 doQue可()方法、 doQueryCursor()方法、 doFlushStatement()方法,其餘的功 能在 BaseExecutor 中實現。

    1.1 Executor 接口中定義的方法如下:

/**
 * @author Clinton Begin
 * Mybatis核心接口之一,定義了數據庫操作最基本的方法。
 * Session的功能都是基於他來實現。
 */
public interface Executor {

  ResultHandler NO_RESULT_HANDLER = null;

  //執行增刪改的三種類型SQL語句
  int update(MappedStatement ms, Object parameter) throws SQLException;

  //執行select類型的sql語句,返回結果爲集合對象
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
  //執行select類型的sql語句,返回結果爲集合對象 這個方法在實現類中還是調用上面的方法執行
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

  //執行select類型的sql語句,返回結果爲遊標
  <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
  //批量執行SQL語句
  List<BatchResult> flushStatements() throws SQLException;
  //提交事務
  void commit(boolean required) throws SQLException;
  //回滾事務
  void rollback(boolean required) throws SQLException;
  //創建緩存key值 
  CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
  //是否緩存
  boolean isCached(MappedStatement ms, CacheKey key);
  //清空一級緩存
  void clearLocalCache();
  //延遲加載一級緩存數據
  void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
  //獲取事務對象
  Transaction getTransaction();
  //關閉Executor對象
  void close(boolean forceRollback);
   //對象是否關閉
  boolean isClosed();
  //
  void setExecutorWrapper(Executor executor);

}

1.2 BaseExecutor 抽象類

  抽象類,實現了executor接口的大部分方法,主要提供了緩存管理和事務管理的能力,其他子類需要實現的抽象方法doUpdate,doQuery等方法;

    下圖是BaseExecutor的query()方法執行過程:

                                 

 

BaseExecutor 中各個字段的含義如下:

public abstract class BaseExecutor implements Executor {

  private static final Log log = LogFactory.getLog(BaseExecutor.class);

  protected Transaction transaction;//事務對象,實現事務的提交、回滾和關閉操作
  protected Executor wrapper;//裝飾器模式 封裝Executor對象

  protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;//延遲加載隊列
  protected PerpetualCache localCache;//一級緩存的實現
  protected PerpetualCache localOutputParameterCache;//一級緩存用於緩存輸出的結果
  protected Configuration configuration;//全局唯一configuration對象的引用

  protected int queryStack;//用於嵌套查詢的的層數
  private boolean closed;

  protected BaseExecutor(Configuration configuration, Transaction transaction) {
    this.transaction = transaction;
    this.deferredLoads = new ConcurrentLinkedQueue<>();
    this.localCache = new PerpetualCache("LocalCache");
    this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
    this.closed = false;
    this.configuration = configuration;
    this.wrapper = this;
  }

      下面是query實現方法:代碼中調用的 query()方法的另一重載的具體實現,該重載會根據前面創建的 CacheKey 對象查詢一級緩存,如果緩存命中則將緩存中記錄的結果對象返回,如果緩存未命中, 則調用 doQuery()方法完成數據庫的查詢操作並得到結果對象,之後將結果對象記錄到一級緩存 中。

@Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //獲取sql語句信息,包括佔位符,參數等信息
    BoundSql boundSql = ms.getBoundSql(parameter);
    //創建CacheKey 對象,該值作爲一級緩存的KEY
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  }

  @SuppressWarnings("unchecked")
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {//檢查當前executor對象是否關閉
      throw new ExecutorException("Executor was closed.");
    }
    //非嵌套查詢,並且<select>節點配置的 flushCache 屬性爲 true 時,纔會清空一級緩存 I
    // flushCache 配置項是影響一級緩存中結采對象存活時長的第一個方面
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();//清空一級緩存
    }
    List<E> list;
    try {
      queryStack++;//增加查詢層數
      //查詢一級緩存
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        //針對調用存儲過程的結果處理
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        //緩存未命中,從數據庫加載數據
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {//延遲加載處理
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      //如果當前sql的一級緩存配置爲STATEMENT,查詢完既清空一集緩存
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

1.2.1 一級緩存

     MyBatis 作爲一個功能強大的 ORM 框架,也提供了緩存的功能, 其緩存設計爲兩層結構, 分別爲一級緩存和二級緩存。 

      一級緩存是會話級別的緩存,在 MyBatis 中每創建一個 Sq!Session 對象,就表示開啓一次數據庫會話。在一次會話中,應用程序可能會在短時間內,例如一個事務內,反覆執行完全相 同的查詢語句,如果不對數據進行緩存,那麼每一次查詢都會執行一次數據庫查詢操作,而多 次完全相同的、時間間隔較短的查詢語句得到的結果集極有可能完全相同,這也就造成了數據 庫資源的浪費。
      一級緩存的生命週期與 SqlSession 相同,其實也就與 SqI Session 中封裝的 Executor 對象的 生命週期相同。當調用 Executor對象的 close()方法時,該 Executor 對象對應的一級緩存就變得 不可用。一級緩存中對象的存活時間受很多方面的影響,例如,在調用 Executor.update()方法時, 也會先請空一級緩存。其他影響一級緩存中數據的行爲,我們在分析 BaseExecutor 的具體實現 時會詳細介紹。一級緩存默認是開啓 的, 一般情況下,不需要用戶進行特殊配置。 

 //創建CacheKey 對象,該值作爲一級緩存的KEY
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);

      該 CacheKey 對象由 MappedStatement 的 id、對應的 offset 和 limit、 SQL 語句(包含“?”佔位符)、用戶傳遞的實參以及 Environment 的 id 這五部分構成。 

 @Override
  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    CacheKey cacheKey = new CacheKey(); //創建 CacheKey 對象
    cacheKey.update(ms.getId());//將 MappedStatement 的 id 添加到 CacheKey 對象中
    cacheKey.update(rowBounds.getOffset());//將 offset 添加到 CacheKey 對象中
    cacheKey.update(rowBounds.getLimit());//將 limit 添加到 CacheKey 對象中
    cacheKey.update(boundSql.getSql());//將 SQL 語句添加到 CacheKey 對象中
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
    // mimic DefaultParameterHandler logic
    //獲取用戶傳入的實參,並添加CacheKey 對象中
    for (ParameterMapping parameterMapping : parameterMappings) {
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        String propertyName = parameterMapping.getProperty();
        if (boundSql.hasAdditionalParameter(propertyName)) {
          value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {
          value = null;
        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else {
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        cacheKey.update(value);//將實參添加到 CacheKey 對象中
      }
    }
    //如果 Environment 的 id 不爲空,則將其添加到 CacheKey 中
    //區分不同的數據源
    if (configuration.getEnvironment() != null) {
      // issue #176
      cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }

1.2.2 事務相關處理

   BaseExecutor.commit()方法首先會清空一級緩存、調用 flushStatements()方法,最後才根據 參數決定是否真正提交事務。 commit()方法的實現如下:

  @Override
  public void commit(boolean required) throws SQLException {
    if (closed) {
      throw new ExecutorException("Cannot commit, transaction is already closed");
    }
    //情況一級緩存
    clearLocalCache();
    //執行緩存的 SQL 語句,其中調用了 flushStatements(false )方法
    flushStatements();
    if (required) {//判斷是否需要提交事務
      transaction.commit();
    }
  }

      BaseExecutor.rollback()方法的實現與 commit()實現類似, 同樣會根據參數決定是否真正回 滾事務,區別是其中調用的是 flushStatements()方法的 isRollBack 參數爲 true, 這就會導致 Executor 中緩存的 SQL 語句全部被忽略(不會被髮送到數據庫執行)。
     BaseExecutor.close()方法首先會調用 rollback()方法忽略緩存的 SQL 語句,之後根據參數決 定是否關閉底層的數據庫連接

1.2.3 真正查詢數據庫查詢數據:

    真正查收數據庫調用抽象方法doQuery,方法查詢數據庫並返回結果,使用了模板模式,其可選的實現包括:SimpleExecutor、ReuseExecutor、BatchExecutor。

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds,
                                        ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    //在緩存中添加佔位符
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      //調用抽象方法doQuery,方法查詢數據庫並返回結果,
      // 可選的實現包括:SimpleExecutor、ReuseExecutor、BatchExecutor
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);//在緩存中刪除佔位符
    }
    localCache.putObject(key, list);//將真正的結果對象添加到一級緩存
    if (ms.getStatementType() == StatementType.CALLABLE) {//如果是調用存儲過程
      localOutputParameterCache.putObject(key, parameter);//緩存輸出類型結果參數
    }
    return list;
  }

      BaseExecutor. update()方法負責執行 insert、update、delete 三類 SQL語句,它是調用 doUpdate() 模板方法實現的。在調用 doUpdate()方法之前會清空緩存,因爲執行 SQL 語句之後,數據庫中 的數據已經更新, 一級緩存的內容與數據庫中的數據可能己經不一致了,所以需要調用 clearLocalCache()方法清空一級緩存中的“髒數據”。

二 、Executor的實現類解讀

  1.    SimpleExecutor :默認配置,使用PrepareStatement對象訪問數據庫,每次訪問都要創建新的PrepareStatement對象;
  2.    ReuseExecutor:使用預編譯PrepareStatement對象訪問數據庫,訪問時,會重用緩存中的statement對象;
  3.    BatchExecutor:實現批量執行多條SQL語句的能力;
  4.    CachingExecutor :是一個 Executor 接口的裝飾器,它爲 Executor 對象增加了二級緩存的相關 功能.

 2.1 SimpleExecutor

        SimpleExecutor 繼承了 BaseExecutor 抽象類, 它是最簡單的 Executor 接口實現。正如前面 所說, Executor 使用了模板方法模式, 一級緩存等固定不變的操作都封裝到了 BaseExecutor 中, 在 SimpleExecutor 中就不必再關心一級緩存等操作,只需要專注實現4個基本方法的實現即可。
   首先來看 SimpleExecutor.doQuery()方法的具體實現:

 @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();//獲取獲取configuration對象
      //創建 StatementHandler 對象,實際返回的是 RoutingStatementHandler 對象,
      //其中根據 MappedStatement.statementType 選擇具體的 StatementHandler 實現
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //StatementHandler對象創建stmt,並使用parameterHandler對佔位符進行處理
      stmt = prepareStatement(handler, ms.getStatementLog());
      //通StatementHandler對象調用ResultSetHandler將結果集轉化爲指定對象返回
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

        1、創建 StatementHandler 對象(這個後繼會有介紹)。封裝了JDBC Statement 操作,負責對 JDBC Statement 的操作,如設置參數、將Statement 結果集轉換成 List 集合。

        實際返回的是 RoutingStatementHandler 對象,其中根據 MappedStatement.statementType 選擇具體的 StatementHandler 實現。

//創建 StatementHandler 對象,實際返回的是 RoutingStatementHandler 對象,
      //其中根據 MappedStatement.statementType 選擇具體的 StatementHandler 實現
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);


  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
	//創建RoutingStatementHandler對象,實際由statmentType來指定真實的StatementHandler來實現
	StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
	//生產攔截器對象 方便擴展
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }
/**
 * Excutor組件真正實例化的子類,使用靜態代理模式,根據上下文決定創建哪個具體實體類;
 * @author Clinton Begin
 */
public class RoutingStatementHandler implements StatementHandler {

  private final StatementHandler delegate;//底層封裝的真正的StatementHandler對象

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    //RoutingStatementHandler最主要的功能就是根據mappedStatment的配置,生成一個對應的StatementHandler對象並賦值給delegate
    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }

 ......
}
2、StatementHandler對象創建stmt,並使用parameterHandler對佔位符進行處理
 //StatementHandler對象創建stmt,並使用parameterHandler對佔位符進行處理
      stmt = prepareStatement(handler, ms.getStatementLog());

  //創建Statement
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //獲取connection對象的動態代理,添加日誌能力;
    Connection connection = getConnection(statementLog);
    //通過不同的StatementHandler,利用connection創建(prepare)Statement
    stmt = handler.prepare(connection, transaction.getTimeout());
    //使用parameterHandler處理佔位符
    handler.parameterize(stmt);
    return stmt;
  }

  protected Connection getConnection(Log statementLog) throws SQLException {
    //獲取數據庫連接
    Connection connection = transaction.getConnection();
    //如果是Debug 則獲取其動態代理對象添加打印日誌的功能
    if (statementLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }

2.2 ReuseExecutor

     使用預編譯PrepareStatement對象訪問數據庫,訪問時,會重用緩存中的statement對象;

       ReuseExecutor 提供了 Statement 重用的功能, ReuseExecutor 中通過 statementMap 字段 (HashMap<String, Statement>類型) 緩存使用過的 Statement 對象, key 是 SQL 語句, value 是 SQL 對應的 Statement 對象。                                                    ReuseExecutor.doQuery() 、doUpdate()等方法的實現與 SimpleExecutor 中對 應方法的實現一樣,區別在於其中調用的 preparestatement()方法, SimpleExecutor 每次都會通過 JDBC Connection 創建新的 Statement 對象,而 ReuseExecutor 則會先嚐試重用 StaternentMap 中 緩存的 Statement 對象。
      ReuseExecutor. prepareStatement()方法的具體實現如下:

 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    BoundSql boundSql = handler.getBoundSql();
    String sql = boundSql.getSql();//獲取sql語句
    if (hasStatementFor(sql)) {//根據sql語句檢查是否緩存了對應的Statement
      stmt = getStatement(sql);//獲取緩存的Statement
      applyTransactionTimeout(stmt);//設置新的超時時間
    } else {//緩存中沒有statment,創建statment過程和SimpleExecutor類似
      Connection connection = getConnection(statementLog);
      stmt = handler.prepare(connection, transaction.getTimeout());
      putStatement(sql, stmt);//放入緩存中
    }
    //使用parameterHandler處理佔位符
    handler.parameterize(stmt);
    return stmt;
  }

2.3 BatchExecutor(這裏就先不做具體分析)

      應用系統在執行一條 SQL 語句時,會將 SQL 語句以及相關參數通過網絡發送到數據庫系 統。對於頻繁操作數據庫的應用系統來說,如果執行一條 SQL 語句就向數據庫發送一次請求, 很多時間會浪費在網絡通信上。使用批量處理的優化方式可以在客戶端緩存多條 SQL 語句,並 在合適的時機將多條 SQL 語句打包發送給數據庫執行,從而減少網絡方面的開銷,提升系統的 性能。 

2.4 CachingExecutor(二級緩存裝飾器)

    CachingExecutor 是一個 Executor 接口的裝飾器,它爲 Executor 對象增加了二級緩存的相關 功能。 在開始介紹 CachingExecutor 的具體實現之前,先來簡單介紹一下 MyBatis 中的二級緩存 及其依賴的相關組件。

      1) 首先是 mybatis-config.xml 配置文件中的 cacheEnabled 配置,它是二級緩存的總開關。 只有當該配置設置爲 true 時,後面兩項的配置纔會有效果, cacheEnabled 的默認值爲 true。具 體配置如下:

<settings>
		<!-- 這個配置使全局的映射器啓用或禁用緩存 -->
		<setting name="cacheEnabled" value="true" />
</settings>

   2) 映射配置文件的解析流程時提到,映射配置文件中可以配置<cache>節點 或<cached-ref>節點。 

   3 )最後一個配置項是<select>節點中的 useCache 屬性,該屬性表示查詢操作產生的結果 對象是否要保存到二級緩存中。 useCache 屬性的默認值是 true。 

  CachingExecutor.query()方法執行查詢操作的步驟如下:

  •       獲取 BoundSql 對象,創建查詢語句對應的 CacheKey 對象。
  •       檢測是否開啓了二級緩存,如果沒有開啓二級緩存,則直接調用底層 Executor 對象的 query()方法查詢數據庫。如果開啓了二級緩存,則繼續後面的步驟。
  •     檢測查詢操作是否包含輸出類型的參數,如果是這種情況,則報錯。
  •     調用 TransactionalCacheManager.getObject()方法查詢二級緩存,如果二級緩存中查找 到相應的結果對象,則直接將該結果對象返回。
 @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
	//獲取sql語句信息,包括佔位符,參數等信息
    BoundSql boundSql = ms.getBoundSql(parameterObject);
  //拼裝緩存的key值
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

 @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
	//從MappedStatement中獲取二級緩存
    Cache cache = ms.getCache();
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);//從二級緩存中獲取數據
        if (list == null) {
          //二級緩存爲空,纔會調用BaseExecutor.query
          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);
  }

 通過對SimpleExecutor.doQuery()方法的解讀發現,Executor是個指揮官,它在調度三個小弟工作:

  1.      StatementHandler:它的作用是使用數據庫的Statement或PrepareStatement執行操作,啓承上啓下作用;
  2.      ParameterHandler:對預編譯的SQL語句進行參數設置,SQL語句中的的佔位符“?”都對應 BoundSql.parameterMappings集合中的一個元素,在該對象中記錄了對應的參數名稱以及該參數的相關屬性
  3.      ResultSetHandler:對數據庫返回的結果集(ResultSet)進行封裝,返回用戶指定的實體類型;

對於這個三個執行器在下篇文章會有介紹。

 

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