Mybatis執行SQL的4大基礎元件詳解(圖文並茂)

中介軟體興趣圈發表於2019-11-17

1、Executor

sql執行器,其對應的類全路徑:org.apache.ibatis.executor.Executor。

1.1 Executor類圖

在這裡插入圖片描述

  • Executor 執行器根據介面,定義update(更新或插入)、query(查詢)、commit(提交事務)、rollback(回滾事務)。接下來簡單介紹幾個重要方法:

    • int update(MappedStatement ms, Object parameter) throws SQLException 更新或插入方法,其引數含義如下:、 1)MappedStatement ms:SQL對映語句(Mapper.xml檔案每一個方法對應一個MappedStatement物件) 2)Object parameter:引數,通常是List集合。
    • < E> List< E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) 查詢方法,其引數含義如下: 1)RowBounds:行邊界,主要值分頁引數limit、offset。 2)ResultHandler resultHandler:結果處理器。
    • CacheKey createCacheKey(MappedStatement ms, Object parameterObj, RowBounds bounds, BoundSql bSql) 建立快取Key,Mybatis一二級快取的快取Key,可以看出Key由上述4個引數來決定。 1)BoundSql boundSql:可以通過該物件獲取SQL語句。
  • CachingExecutor 支援結果快取的SQL執行器,注意其設計模式的應用,該類中,會持有Executor的一個委託物件,CachingExecutor關注與快取特定的邏輯,其最終的SQL執行由其委託物件來實現,即其內部的委託物件為BaseExecutor的實現類。

  • BaseExecutor Executor的基礎實現類,該類為抽象類,關於查詢、更新具體的實現由其子類來實現,下面4個都是其子類。

  • SimpleExecutor 簡單的Executor執行器。

  • BatchExecutor 支援批量執行的Executor執行器。

  • ClosedExecutor 表示一個已關閉的Executor。

  • ReuseExecutor 支援重複使用Statement,以SQL為鍵,快取Statement物件。

1.2 建立Executor

在Mybatis中,Executor的建立由Configuration物件來建立,具體的程式碼如下:

Configuration#newExecitor

public Executor newExecutor(Transaction transaction) {
  return newExecutor(transaction, defaultExecutorType);   // @1
}


public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  executorType = executorType == null ? defaultExecutorType : executorType;
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor executor;
  if (ExecutorType.BATCH == executorType) {   // @2
    executor = new BatchExecutor(this, transaction);
  } else if (ExecutorType.REUSE == executorType) {
    executor = new ReuseExecutor(this, transaction);
  } else {
    executor = new SimpleExecutor(this, transaction);
  }
  if (cacheEnabled) { // @3
    executor = new CachingExecutor(executor);
  }
  executor = (Executor) interceptorChain.pluginAll(executor);  // @4
  return executor;
}
複製程式碼

從上面的程式碼可以看出,Executor的建立由如下三個關鍵點: 程式碼@1:預設的ExecutorType為ExecutorType.SIMPLE,即預設建立的Executory為SimpleExecutor。 程式碼@2:根據executorType的值建立對應的Executory。 程式碼@3:如果cacheEnabled為true,則建立CachingExecutory,然後在其內部持有上面建立的Executor,cacheEnabled預設為true,則預設建立的Executor為CachingExecutor,並且其內部包裹著SimpleExecutor。 程式碼@4:使用InterceptorChain.pluginAll為executor建立代理物件,即Mybatis的拆件機制,將在該系列文章中詳細介紹。

2、StatementHandler

在學習StatementHandler之前,我們先來回顧一下JDBC相關的知識。JDBC與語句執行的兩大主流物件:java.sql.Statement、java.sql.PrepareStatement物件大家應該不會陌生,該物件的execute方法就是執行SQL語句的入口,通過java.sql.Connection物件建立Statement物件。Mybatis的StatementHandler,是Mybatis建立Statement物件的處理器,即StatementHandler會接管Statement物件的建立。

2.1 StatementHandler類圖

在這裡插入圖片描述

  • StatementHandler 根介面,我們重點關注一下其定義的方法:

    • Statement prepare(Connection connection) 建立Statement物件,即該方法會通過Connection物件建立Statement物件。
    • void parameterize(Statement statement) 對Statement物件引數化,特別是PreapreStatement物件。
    • void batch(Statement statement) 批量執行SQL。
    • int update(Statement statement) 更新操作。
    • < E> List< E> query(Statement statement, ResultHandler resultHandler) 查詢操作。
    • BoundSql getBoundSql() 獲取SQL語句。
    • ParameterHandler getParameterHandler() 獲取對應的引數處理器。
  • BaseStatementHandler StatementHandler的抽象實現類,SimpleStatementHandler、PrepareStatementHandler、CallableStatementHandler是其子類。 我們來一一看一下其示例變數:

    • Configuration configuration Mybatis全域性配置物件。
    • ObjectFactory objectFactory 物件工廠。
    • TypeHandlerRegistry typeHandlerRegistry 型別註冊器。
    • ResultSetHandler resultSetHandler 結果集Handler。
    • ParameterHandler parameterHandler 引數處理器Handler。
    • Executor executor SQL執行器。
    • MappedStatement mappedStatement SQL對映語句(Mapper.xml檔案每一個方法對應一個MappedStatement物件)
    • RowBounds rowBounds 行邊界,主要值分頁引數limit、offset。
    • BoundSql boundSql 可以通過該物件獲取SQL語句。
  • SimpleStatementHandler 具體的StatementHandler實現器,java.sql.Statement物件建立處理器。

  • PrepareStatementHandler java.sql.PrepareStatement物件的建立處理器。

  • CallableStatementHandler java.sql.CallableStatement物件的建立處理器,可用來執行儲存過程呼叫的Statement。

  • RoutingStatementHandler StatementHandler路由器,我們看一下其構造方法後,就會對該類瞭然於胸。

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

  switch (ms.getStatementType()) { // @1
    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());
  }

}
複製程式碼

原來是會根據MappedStatement物件的statementType建立對應的StatementHandler。

2.2 建立StatementHandler

Configuration#newStatementHandler

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); // @1
  statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); // @2
  return statementHandler;
}
複製程式碼

該方法的兩個關鍵點如下: 程式碼@1:建立RoutingStatementHandler物件,在其內部再根據SQL語句的型別,建立對應的StatementHandler物件。 程式碼@2:對StatementHandler引入拆件機制,該部分將在該專題的後續文章中會詳細介紹,這裡暫時跳過。

3、ParameterHandler

引數處理器。同樣我們先來看一下其類圖。

3.1 ParameterHandler類圖

在這裡插入圖片描述
這個比較簡單,就是處理PreparedStatemet介面的引數化處理,也可以順便看一下其呼叫鏈(該部分會在下一篇中詳細介紹)。
在這裡插入圖片描述

3.2 建立ParameterHandler

Configuration#newParameterHandler

public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
  ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
  parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);  // @1
  return parameterHandler;
}
複製程式碼

同樣該介面也支援外掛化機制。

4、ResultSetHandler

處理結果的Handler。我們同樣看一下其類圖。

4.1 ResultSetHandler類圖

在這裡插入圖片描述
處理Jdbc ResultSet的處理器。

4.2 ResultSetHandler建立

Configuration#newResultSetHandler

public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
    ResultHandler resultHandler, BoundSql boundSql) {
  ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
  resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
  return resultSetHandler;
}
複製程式碼

同樣支援外掛化機制,我們也稍微再看一下其呼叫鏈:

在這裡插入圖片描述
可以看出其呼叫的入口為SQL執行時。

本文作為下一篇《原始碼分析Mybatis整合ShardingJdbc SQL執行流程》的前置篇,重點介紹Executor、StatementHandler、ParameterHandler、ResultSetHandler的具體職責,以類圖為基礎並詳細介紹其核心方法的作用,然後詳細介紹了這些物件是如何建立,並引出Mybatis拆件機制。


作者介紹:《RocketMQ技術內幕》作者,維護公眾號:中介軟體興趣圈,目前主要發表了原始碼閱讀java集合、JUC(java併發包)、Netty、ElasticJob、Mycat、Dubbo、RocketMQ、mybaits等系列原始碼。

在這裡插入圖片描述

相關文章