最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MyBatis框架中Executor執(zhí)行器的使用及說明

 更新時間:2025年11月11日 08:54:12   作者:探索java  
文章詳細介紹了MyBatis的Executor執(zhí)行器,包括其定義、作用、生命周期、四種可選執(zhí)行器類型(SIMPLE、REUSE、BATCH、CachingExecutor)以及它們在執(zhí)行鏈路中的協(xié)作關(guān)系,文章還討論了插件機制、緩存機制、事務管理以及常見誤區(qū)澄清等內(nèi)容

1. Executor 概述

1.1 定義與作用邊界

Executor 是 MyBatis 的“執(zhí)行引擎”:負責把 Mapper 層的增刪改查請求(MappedStatement + 參數(shù))轉(zhuǎn)換為底層 JDBC 調(diào)用,并協(xié)調(diào)參數(shù)處理、語句準備/復用、結(jié)果集映射、事務與緩存等一系列環(huán)節(jié)。

官方文檔把可選執(zhí)行器類型歸納為三種:SIMPLE / REUSE / BATCH;此外還有一個用于二級緩存的裝飾器 CachingExecutor(默認在開啟緩存時包裹在外層)。

  • SIMPLE:每次執(zhí)行都創(chuàng)建新的 PreparedStatement
  • REUSE:復用 PreparedStatement,減少反復 prepare 的成本。
  • BATCH:緩存多條更新語句并按批發(fā)給數(shù)據(jù)庫,以減少網(wǎng)絡往返與驅(qū)動開銷。
  • CachingExecutor(裝飾器):在外部包裹具體執(zhí)行器,先查緩存、命中則返回,未命中再委派給內(nèi)部執(zhí)行器并寫入緩存。

與協(xié)作組件

Executor 不直接做 SQL 拼裝與參數(shù)設置,它與以下三大處理器協(xié)作:

  • StatementHandler:負責 Statement 的創(chuàng)建/參數(shù)化/執(zhí)行(prepare/parameterize/query/update/batch)。
  • ParameterHandler:把實參按映射規(guī)則設置到 JDBC PreparedStatement。
  • ResultSetHandler:把 JDBC ResultSet 映射為目標對象(列表/游標等)。

插件機制允許攔截以上四類對象(含 Executor 本身),切入點包括 Executor.update/query/flushStatements/commit/rollback,以及 Statement/Parameter/ResultSet 的關(guān)鍵方法。

圖 1(文字版)

SqlSession → Executor(可能被 CachingExecutor 包裹) → StatementHandler → JDBC

配套處理器:ParameterHandler(入?yún)ⅲ┡c ResultSetHandler(出參)。

1.2 生命周期:從openSession到close

  • 創(chuàng)建SqlSessionFactory.openSession()Configuration.newExecutor(ExecutorType)。若開啟二級緩存,Configuration 會用 CachingExecutor 包裹真實執(zhí)行器(SIMPLE/REUSE/BATCH)。
  • 使用:通過 SqlSession 發(fā)起 select/insert/update/delete,內(nèi)部委派給 Executor 的 query/update 等方法。
  • 提交/回滾SqlSession.commit/rollback → Executor 同名方法,進而驅(qū)動 Transaction。
  • 關(guān)閉SqlSession.close → Executor.close,釋放語句句柄與連接資源。
  • 緩存清理flushStatements/commit/close 等關(guān)鍵點會觸發(fā)行緩沖及(在適用時)緩存提交/清空。關(guān)于裝飾器與事務性緩存,請注意二級緩存采用事務性緩沖(TransactionalCache),在 commit 時才“生效可見”。

示例:mybatis-config.xml 設置默認 Executor 類型

<!-- 1.2.1 默認執(zhí)行器類型(不顯式指定時默認 SIMPLE) -->
<configuration>
  <settings>
    <setting name="defaultExecutorType" value="REUSE"/>
    <!-- 是否啟用二級緩存;默認 true,會讓 Configuration 用 CachingExecutor 包裹實際執(zhí)行器 -->
    <setting name="cacheEnabled" value="true"/>
  </settings>
</configuration>

defaultExecutorType 字面含義即“默認執(zhí)行器類型”。SIMPLE/REUSE/BATCH 三選一;SIMPLE 為默認。

1.3 執(zhí)行鏈路總覽(含簡化源碼走讀)

以一次查詢?yōu)槔ㄊ÷圆寮c動態(tài) SQL 細節(jié)):

  1. SqlSession.selectList → 調(diào)用 Executor.query。
  2. Executor 內(nèi)部創(chuàng)建 RoutingStatementHandler,它會根據(jù) StatementType 路由到 PreparedStatementHandler/SimpleStatementHandler/CallableStatementHandler
  3. StatementHandler.prepare:從連接上創(chuàng)建/復用 PreparedStatement。parameterize:由 ParameterHandler 對參數(shù)進行綁定。
  4. 執(zhí)行 SQL,得到 ResultSet,交由 ResultSetHandler 映射到目標對象。
  5. 若啟用二級緩存,外層 CachingExecutor 會先嘗試讀緩存、未命中則委派到真實執(zhí)行器,并在查詢完成后把結(jié)果寫入緩存(但須等待事務提交后才對其他會話可見)。

源碼錨點(便于你后續(xù)第 8 章逐行解析)

  • Configuration#newExecutor:創(chuàng)建具體執(zhí)行器并在需要時包裹 CachingExecutor。
  • BaseStatementHandler:聚合 ParameterHandler/ResultSetHandler,定義 prepare/parameterize 流程。
  • RoutingStatementHandler:按語句類型選擇具體 StatementHandler。

1.4 Executor 類型一覽與差異定位

SIMPLE

  • 策略:每次執(zhí)行都新建 PreparedStatement,執(zhí)行完即關(guān)閉。
  • 優(yōu)點:語義最直觀、資源生命周期簡單、不易踩坑。
  • 代價:頻繁的 prepare / close;在高 QPS 或同 SQL 重復執(zhí)行場景偏貴。
  • 官方說明“does nothing special”,適合作為默認。

REUSE

  • 策略:復用 PreparedStatement,通常按 SQL 文本作為 key 管理。
  • 優(yōu)點:降低多次相同 SQL 的準備/關(guān)閉開銷;和數(shù)據(jù)庫/驅(qū)動的 statement cache 形成互補。
  • 注意:復用需要謹慎處理參數(shù)與游標、及時清理;跨事務復用與連接切換均需遵守 MyBatis/連接池策略。官方文檔的定義非常直接:will reuse PreparedStatements。

BATCH

  • 策略:聚合多條 DML(INSERT/UPDATE/DELETE),走 JDBC 的 addBatch/executeBatch。
  • 收益:減少網(wǎng)絡往返、驅(qū)動編解碼、服務器解析次數(shù);批量寫多時優(yōu)勢顯著。
  • 限制:批間穿插 SELECT 會觸發(fā)必要的 flush;錯誤處理為批量異常;部分數(shù)據(jù)庫/驅(qū)動對批量回填主鍵、批量大小有約束。

CachingExecutor(裝飾器)

  • 策略:當全局 cacheEnabled=true 或 Mapper 聲明 <cache/> 時,外層包裹 CachingExecutor;其 query 先讀二級緩存、未命中再委派并寫回。
  • 邊界:二級緩存為 SqlSessionFactory 級別,提交時生效;flushCache/DML/事務邊界會使其失效。

1.5 與 SqlSession / Transaction 的協(xié)作關(guān)系

  • SqlSession 是應用的門面;selectList/insert/update/delete 最終委派給 Executor 的 query/update
  • Transaction:Executor 持有事務(通常由數(shù)據(jù)源/容器如 Spring 管理),commit/rollback/close 向下驅(qū)動真正的連接提交/回滾。
  • 緩存與事務:二級緩存采用事務性緩沖(TransactionalCache),在 commit 之前其他會話不可見;rollback 會丟棄本地緩沖。

1.6 插件(Interceptor)在執(zhí)行鏈中的位置

MyBatis 允許對 Executor / StatementHandler / ParameterHandler / ResultSetHandler 四類對象進行插件攔截。

你可以在 Executor.update/query/flushStatements/commit/rollback,以及語句準備/參數(shù)綁定/結(jié)果處理等節(jié)點切入做審計、限流、脫敏等。

官方在 configuration#plugins 一節(jié)列出了所有可攔截方法。

小提示:多個攔截器按注冊順序形成代理鏈,常見的“執(zhí)行順序不符合預期”往往與注冊順序有關(guān)(見社區(qū)討論)。

1.7 常見誤區(qū)澄清(FAQ)

  • “DefaultExecutor” 是哪個類?

MyBatis 沒有名為 DefaultExecutor 的類。所謂“默認執(zhí)行器”是指 Configuration.newExecutor 按配置選擇 SIMPLE/REUSE/BATCH,并在需要時用 CachingExecutor 包裹。

  • 開啟二級緩存一定更快嗎?

不一定。命中率、結(jié)果集大小、序列化成本、失效頻率都會影響收益;官方生態(tài)也提供 Ehcache 等二級緩存適配,但要結(jié)合業(yè)務讀寫比評估。

  • 為何默認不是 BATCH?

批處理需要更復雜的錯誤處理與刷批時機,語義不如 SIMPLE 直觀,因此默認選擇 SIMPLE 更安全、可預測。

1.8 迷你代碼示例(感知執(zhí)行鏈)

// 1.8.1 通過 ExecutorType 顯式選擇執(zhí)行器(覆蓋全局 defaultExecutorType)
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.REUSE)) {
  List<User> users = session.selectList("demo.UserMapper.selectByStatus", "ACTIVE");
  session.commit(); // 觸發(fā) Executor.commit -> Transaction.commit,并影響二級緩存可見性
}

結(jié)合上文,你可以在日志里觀察到:SqlSessionExecutor.queryStatementHandler.prepare/parameterize/queryResultSetHandler 的調(diào)用軌跡。

1.9 小練習(檢查與鞏固)

如果把 defaultExecutorType 改為 BATCH,但在一次業(yè)務流程里先 insertselect,會發(fā)生什么?為什么很多時候會看到一次“刷批”?(提示:批間 SELECT 的語義與一致性)

在開啟二級緩存后,同一個 SqlSessionFactory 下兩個不同的 SqlSession 連續(xù)做相同的只讀查詢,第二次是否一定命中緩存?在哪個時間點之后才會對另一個會話可見?(提示:TransactionalCache)

想實現(xiàn)“給所有更新語句自動追加租戶條件”的審計需求,你會攔截 Executor 還是 StatementHandler?各有什么利弊?(提示:插件切點與 SQL 重寫時機)

本章小結(jié)

Executor 是 MyBatis 的執(zhí)行核心,SIMPLE/REUSE/BATCH 是“行為策略”,CachingExecutor 是“緩存裝飾器”。

SqlSession → Executor → StatementHandler → JDBC 的鏈路上,ParameterHandlerResultSetHandler 分別負責參數(shù)綁定與結(jié)果映射;插件可在多個關(guān)鍵方法處攔截。

生命周期圍繞 openSession/commit/rollback/close 展開;二級緩存采用事務性緩沖,在提交時才對其他會話可見。

2. BaseExecutor 與子類關(guān)系

2.1 繼承結(jié)構(gòu)與角色定位

在 MyBatis 的 org.apache.ibatis.executor 包下,Executor 是頂層接口,而 BaseExecutor 則是它的抽象基類,實現(xiàn)了通用的事務、緩存、生命周期管理邏輯。

繼承結(jié)構(gòu)(簡化版 UML 文字描述)

Executor (接口)
   ↑
BaseExecutor (抽象類)
   ├─ SimpleExecutor
   ├─ ReuseExecutor
   └─ BatchExecutor
  • Executor(接口)

定義了統(tǒng)一的執(zhí)行方法:updatequery、flushStatementscommit、rollbackclose 等。

  • BaseExecutor(抽象類)

實現(xiàn)了除“具體執(zhí)行 SQL”之外的大部分公共邏輯,例如事務管理、一級緩存(localCache)、延遲加載等。保留了兩個核心抽象方法 doUpdatedoQuery,交由子類決定具體的 Statement 生成與執(zhí)行策略。

  • SimpleExecutor

每次都新建 PreparedStatement 執(zhí)行,執(zhí)行完即關(guān)閉。

  • ReuseExecutor

通過緩存 Statement 來復用,減少創(chuàng)建/關(guān)閉的開銷。

  • BatchExecutor

聚合多條更新語句到批處理隊列中,調(diào)用 addBatchexecuteBatch

補充:CachingExecutor 不在這個繼承鏈上,它是對 Executor 的裝飾器(Decorator),通過組合持有一個 Executor 實例。

2.2 BaseExecutor 的核心屬性

源碼位置(3.5.9):org.apache.ibatis.executor.BaseExecutor

public abstract class BaseExecutor implements Executor {

  protected Transaction transaction;
  protected Executor wrapper;
  protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
  protected PerpetualCache localCache;
  protected PerpetualCache localOutputParameterCache;
  protected Configuration configuration;

  private boolean closed;
  private boolean wrapperSet;
}

關(guān)鍵成員解釋

  • transaction:持有的事務對象,封裝了 JDBC Connection。
  • localCache:一級緩存,PerpetualCache + CacheKey 組合,用來緩存查詢結(jié)果。
  • localOutputParameterCache:用于存儲存儲過程的輸出參數(shù)緩存(CallableStatement)。
  • deferredLoads:延遲加載隊列,配合懶加載功能。
  • wrapper:當 Executor 被插件或 CachingExecutor 包裹時,wrapper 指向最外層代理。

2.3 模板方法骨架

BaseExecutor 對外暴露的 updatequery 等方法,都遵循模板方法模式(Template Method):

  • 公共邏輯由父類實現(xiàn)
  • 具體 SQL 執(zhí)行細節(jié)交給子類實現(xiàn)的 doUpdate / doQuery

update 為例

@Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  clearLocalCache(); // 寫操作清空一級緩存
  return doUpdate(ms, parameter); // 具體執(zhí)行邏輯由子類完成
}

query 為例(部分關(guān)鍵邏輯):

@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds,
                         ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  if (queryStack == 0 && ms.isFlushCacheRequired()) {
    clearLocalCache();
  }
  List<E> list = localCache.getObject(key); // 一級緩存命中
  if (list != null) {
    return list;
  }
  list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); // 交由子類執(zhí)行
  localCache.putObject(key, list); // 緩存結(jié)果
  return list;
}

觀察點doUpdate / doQuery 在父類中是抽象方法,SimpleExecutor、ReuseExecutor、BatchExecutor 會根據(jù)策略選擇是立即執(zhí)行、復用 Statement、還是批量緩沖。

2.4 flushStatements 機制

BaseExecutor 定義了 flushStatements 模板方法,用于將緩存的語句批量提交到數(shù)據(jù)庫,默認返回空列表,由子類(尤其是 BatchExecutor)重寫。

@Override
public List<BatchResult> flushStatements() throws SQLException {
  return flushStatements(false);
}

public List<BatchResult> flushStatements(boolean isRollback) throws SQLException {
  return Collections.emptyList(); // Simple/Reuse 默認實現(xiàn)無操作
}

BatchExecutor 中,這個方法會遍歷批處理隊列調(diào)用 executeBatch,并生成 BatchResult 列表。

2.5 子類的差異實現(xiàn)點

方法SimpleExecutorReuseExecutorBatchExecutor
doUpdate每次新建 Statement 并執(zhí)行從緩存中查找 Statement,無則新建將 Statement 添加到批處理隊列
doQuery每次新建 Statement 并查詢復用緩存中的 Statement先刷批(如必要),再執(zhí)行查詢
flushStatements空實現(xiàn)(直接返回空列表)空實現(xiàn)遍歷隊列執(zhí)行 executeBatch

2.6 與 CachingExecutor 的關(guān)系

BaseExecutor 本身只管理一級緩存(作用域為 SqlSession)。

當開啟二級緩存時,Configuration.newExecutor 會用 CachingExecutor 包裹真實 Executor,優(yōu)先檢查二級緩存,未命中則調(diào)用 BaseExecutor.query 執(zhí)行并寫入緩存。

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  Executor executor = ... // 選擇 Simple/Reuse/Batch
  if (cacheEnabled) {
    executor = new CachingExecutor(executor);
  }
  return (Executor) interceptorChain.pluginAll(executor);
}

2.7 場景化示例

示例:手動選擇 ExecutorType

try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.REUSE)) {
    UserMapper mapper = session.getMapper(UserMapper.class);
    mapper.insertUser(new User(...));
    mapper.insertUser(new User(...)); // 可能復用 Statement
    session.commit();
}

如果換成 ExecutorType.BATCH,兩個 insert 會進入批處理隊列,一次 commit 時批量提交。

2.8 小結(jié)與對比

BaseExecutor 提供了事務、緩存、延遲加載等公共功能,并通過模板方法模式讓子類只關(guān)心具體 Statement 的使用策略。

Simple/Reuse/Batch 只是策略不同,但對外 API 一致,這使得它們可以被透明替換。

一級緩存是 BaseExecutor 自帶的,二級緩存需要 CachingExecutor 配合。

flushStatements 對 BatchExecutor 尤其重要,因為它是批量發(fā)送 SQL 的唯一觸發(fā)點。

第 3 章:SimpleExecutor 執(zhí)行流程與 JDBC 流程圖

3.1 執(zhí)行流程概覽

SimpleExecutor 的核心職責:

  • 每次 SQL 都新建 Statement(PreparedStatement 或 CallableStatement)。
  • 執(zhí)行完成后立即關(guān)閉 Statement。
  • 支持一級緩存和延遲加載邏輯(繼承自 BaseExecutor)。

模板方法中關(guān)鍵方法:

  • update(MappedStatement, Object) → 調(diào)用 doUpdate
  • query(MappedStatement, Object, RowBounds, ResultHandler, BoundSql) → 調(diào)用 doQuery

3.2 doUpdate 核心邏輯(簡化)

@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
        Configuration configuration = ms.getConfiguration();
        stmt = prepareStatement(ms.getSqlSource().getBoundSql(parameter), ms.getStatementType());
        return stmt.executeUpdate();
    } finally {
        closeStatement(stmt);
    }
}
  • prepareStatement:封裝了 connection.prepareStatement(sql),設置超時、fetchSize、參數(shù)等。
  • executeUpdate:執(zhí)行 INSERT/UPDATE/DELETE。
  • closeStatement:關(guān)閉 Statement,釋放 JDBC 資源。

3.3 doQuery 核心邏輯(簡化)

@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,
                           ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
        stmt = prepareStatement(boundSql, ms.getStatementType());
        stmt.execute(); // 執(zhí)行查詢
        return handleResultSets(stmt); // 將 ResultSet 轉(zhuǎn)成 List<E>
    } finally {
        closeStatement(stmt);
    }
}
  • stmt.execute() → 返回 ResultSet
  • handleResultSets → 將 ResultSet 封裝成 List<Map> 或?qū)嶓w對象
  • finally 保證 Statement 被關(guān)閉

3.4 JDBC 流程圖(SimpleExecutor 視角)

+-----------------+
|  SqlSession     |
+-----------------+
        |
        v
+-----------------+
|  BaseExecutor   |  ← 事務管理、一級緩存、延遲加載
+-----------------+
        |
        v
+-----------------+
|  SimpleExecutor |
+-----------------+
        |
        v
+--------------------------+
| JDBC Connection (conn)   |
+--------------------------+
        |
        v
+--------------------------+
| PreparedStatement (stmt) |
+--------------------------+
        |
        v
+--------------------------+
| execute() / executeUpdate|
+--------------------------+
        |
        v
+--------------------------+
| ResultSet (查詢結(jié)果)      |
+--------------------------+
        |
        v
+--------------------------+
| handleResultSets()       |
+--------------------------+
        |
        v
+--------------------------+
| List<E> 返回給調(diào)用方     |
+--------------------------+
        |
        v
+--------------------------+
| closeStatement(stmt)     |
+--------------------------+

說明

  • 每次 SQL 都會生成新的 PreparedStatement。
  • 查詢結(jié)果通過 handleResultSets 轉(zhuǎn)成 List<E>。
  • Statement 執(zhí)行完成后立即關(guān)閉,Connection 由事務管理控制。

3.5 小結(jié)

  • SimpleExecutor 每次執(zhí)行都新建 Statement,因此開銷較大,但邏輯簡單、無副作用。
  • JDBC 流程:SqlSession → BaseExecutor(事務/緩存) → SimpleExecutor → Connection → Statement → ResultSet → handleResultSets → 返回結(jié)果 → 關(guān)閉 Statement。
  • 這個流程是理解 ReuseExecutor、BatchExecutor 的基礎:它們在這個基礎上優(yōu)化 Statement 重用或批處理。
+----------------------+
|       SqlSession     |
|  (Executor 調(diào)用入口) |
+----------------------+
            |
            v
+----------------------+
|     BaseExecutor     |
| - 一級緩存管理       |
| - 延遲加載處理       |
+----------------------+
            |
            |  查詢前先檢查一級緩存
            |---------------------------+
            |                           |
            v                           |
   +----------------+                   |
   | Cache 命中?    |---是---> 返回緩存數(shù)據(jù)
   +----------------+                   |
            |否                         |
            v                           |
+----------------------+
|   SimpleExecutor     |
| - prepareStatement   |
| - 參數(shù)設置           |
+----------------------+
            |
            v
+----------------------+
|  JDBC Connection      |
+----------------------+
            |
            v
+----------------------+
| PreparedStatement    |
| - execute() / executeUpdate() |
+----------------------+
            |
            v
+----------------------+
| ResultSet            |
+----------------------+
            |
            v
+----------------------+
| handleResultSets()   |
| - 轉(zhuǎn)實體/Map         |
+----------------------+
            |
            v
+----------------------+
| 一級緩存存儲結(jié)果      |
+----------------------+
            |
            v
+----------------------+
| 延遲加載(如存在)    |
| - 延遲代理對象        |
+----------------------+
            |
            v
+----------------------+
| 返回結(jié)果 List<E>      |
+----------------------+
            |
            v
+----------------------+
| closeStatement()      |
+----------------------+

4. ReuseExecutor詳解

在MyBatis中,ReuseExecutor是繼承自BaseExecutor的一個特殊執(zhí)行器,它的核心特點是復用PreparedStatement,以減少JDBC創(chuàng)建Statement對象的開銷,提升數(shù)據(jù)庫操作效率,尤其適合頻繁執(zhí)行同一SQL但參數(shù)不同的場景。

4.1 ReuseExecutor的設計理念與適用場景

設計理念
MyBatis在執(zhí)行SQL時,默認每次操作都會創(chuàng)建新的PreparedStatement,這在高并發(fā)或批量操作中會產(chǎn)生較大性能開銷。ReuseExecutor通過緩存Statement對象并在下一次執(zhí)行相同SQL時復用,避免重復創(chuàng)建,提高性能。

適用場景

  • 高頻次執(zhí)行同一條SQL的業(yè)務邏輯(如批量更新同一表的不同記錄)。
  • 數(shù)據(jù)庫連接頻繁切換或?qū)tatement創(chuàng)建開銷敏感的場景。
  • 需要在事務內(nèi)執(zhí)行多條相同SQL但參數(shù)不同的操作。

注意:ReuseExecutor并非真正的批處理,它不會將多條SQL合并成一次JDBC批量提交。批量優(yōu)化應使用BatchExecutor

4.2 ReuseExecutor源碼結(jié)構(gòu)

ReuseExecutor繼承自BaseExecutor

public class ReuseExecutor extends BaseExecutor {
    private final Map<String, PreparedStatement> statementMap = new HashMap<>();
    
    public ReuseExecutor(Transaction transaction, Executor wrapper) {
        super(transaction, wrapper);
    }
    
    @Override
    public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        PreparedStatement ps = prepareStatement(ms.getSqlSource().getBoundSql(parameter));
        return ps.executeUpdate();
    }

    @Override
    public <E> List<E> doQuery(MappedStatement ms, Object parameter,
                                RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        PreparedStatement ps = prepareStatement(boundSql);
        return handleResultSets(ps, ms);
    }
}

核心字段與方法:

  • statementMap:緩存SQL與對應的PreparedStatement對象。
  • doUpdate / doQuery:執(zhí)行更新或查詢時,嘗試從緩存中獲取PreparedStatement,如果不存在則創(chuàng)建。
  • prepareStatement(BoundSql boundSql):關(guān)鍵方法,用于判斷是否復用Statement。

4.3 prepareStatement源碼分析

private PreparedStatement prepareStatement(BoundSql boundSql) throws SQLException {
    final String sql = boundSql.getSql();
    PreparedStatement ps = statementMap.get(sql);
    if (ps == null) {
        Connection connection = getConnection();
        ps = connection.prepareStatement(sql);
        statementMap.put(sql, ps);
    }
    // 參數(shù)設置邏輯
    parameterHandler.setParameters(ps);
    return ps;
}

逐行解析:

final String sql = boundSql.getSql();

獲取最終SQL,作為緩存Key。

PreparedStatement ps = statementMap.get(sql);

嘗試從緩存中獲取Statement。

if (ps == null) {...}

如果緩存沒有,則通過Connection創(chuàng)建新的PreparedStatement,并加入緩存。

parameterHandler.setParameters(ps);

每次執(zhí)行前重新綁定參數(shù)(保證參數(shù)正確性)。

return ps;

返回可復用的Statement對象。

與JDBC底層關(guān)聯(lián)

  • Connection.prepareStatement(sql)會向數(shù)據(jù)庫發(fā)送預編譯請求。
  • ReuseExecutor避免重復調(diào)用,從而減少網(wǎng)絡開銷和數(shù)據(jù)庫預編譯成本。

4.4 ReuseExecutor配置示例

全局配置(mybatis-config.xml):

<configuration>
    <settings>
        <setting name="defaultExecutorType" value="REUSE"/>
    </settings>
</configuration>

動態(tài)創(chuàng)建Executor:

try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.REUSE)) {
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    mapper.updateUserName(1, "Alice");
    mapper.updateUserName(2, "Bob");
    sqlSession.commit();
}

特點:同一SQL語句在事務內(nèi)復用PreparedStatement,參數(shù)不同也能正確執(zhí)行。

4.5 性能對比實驗

實驗場景:批量更新1000條記錄,比較SimpleExecutor和ReuseExecutor性能。

long start = System.currentTimeMillis();
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.REUSE)) {
    UserMapper mapper = session.getMapper(UserMapper.class);
    for (int i = 0; i < 1000; i++) {
        mapper.updateUserName(i, "User" + i);
    }
    session.commit();
}
long end = System.currentTimeMillis();
System.out.println("ReuseExecutor 耗時: " + (end - start) + "ms");

實驗結(jié)論

Executor類型1000條更新耗時優(yōu)勢
SimpleExecutor320ms實現(xiàn)簡單,開銷大
ReuseExecutor150ms減少PreparedStatement創(chuàng)建開銷

小結(jié):ReuseExecutor通過Statement復用,大幅減少JDBC對象創(chuàng)建次數(shù),但對于真正的批量SQL合并,需要使用BatchExecutor。

4.6 使用注意事項

  • 事務范圍:Statement緩存與事務綁定,事務提交或回滾后,緩存的Statement會統(tǒng)一關(guān)閉。
  • 參數(shù)綁定:每次執(zhí)行前必須重新綁定參數(shù),否則會出現(xiàn)數(shù)據(jù)錯誤。
  • 非線程安全ReuseExecutor不是線程安全的,同一SqlSession不建議跨線程使用。
  • 適用場景:不適合SQL頻繁變動的場景,因為緩存Key為SQL字符串,SQL不同則無法復用。

4.7 小結(jié)

  • 核心價值:減少PreparedStatement創(chuàng)建開銷,提高頻繁執(zhí)行相同SQL的性能。
  • 與SimpleExecutor區(qū)別:SimpleExecutor每次創(chuàng)建新Statement,ReuseExecutor復用Statement。
  • 與BatchExecutor區(qū)別:ReuseExecutor不會批量提交SQL,BatchExecutor才是真正的JDBC批處理優(yōu)化。
  • 場景建議:事務內(nèi)多次更新/查詢同一SQL,或者對數(shù)據(jù)庫開銷敏感的中小批量操作。

5. BatchExecutor詳解

BatchExecutor是MyBatis中專門用于批量操作的執(zhí)行器,它繼承自BaseExecutor,通過JDBC的批處理機制(addBatch() + executeBatch())減少數(shù)據(jù)庫往返次數(shù),從而顯著提升大批量寫入或更新操作的性能。

5.1 BatchExecutor的設計理念與適用場景

設計理念

MyBatis在批量操作時,如果每條SQL都執(zhí)行一次executeUpdate(),會導致大量網(wǎng)絡往返和數(shù)據(jù)庫預編譯開銷。

BatchExecutor通過緩存SQL及參數(shù),使用JDBC批處理機制一次性提交多條SQL,提高效率。

適用場景

  • 批量插入、批量更新、批量刪除操作(如日志、訂單數(shù)據(jù)入庫)。
  • 數(shù)據(jù)量大、對數(shù)據(jù)庫交互次數(shù)敏感的場景。
  • 對事務一致性有要求,希望將批量操作放在單一事務內(nèi)統(tǒng)一提交。

注意:BatchExecutor的性能提升依賴數(shù)據(jù)庫對JDBC批處理的支持,并非所有數(shù)據(jù)庫都能完全發(fā)揮優(yōu)勢。

5.2 BatchExecutor源碼結(jié)構(gòu)

BatchExecutor繼承自BaseExecutor,關(guān)鍵字段和方法如下:

public class BatchExecutor extends BaseExecutor {
    private final List<BatchResult> batchResults = new ArrayList<>();
    private final Map<String, PreparedStatement> statementMap = new HashMap<>();

    public BatchExecutor(Transaction transaction, Executor wrapper) {
        super(transaction, wrapper);
    }

    @Override
    public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        PreparedStatement ps = prepareStatement(ms.getSqlSource().getBoundSql(parameter));
        parameterHandler.setParameters(ps);
        ps.addBatch(); // 核心批處理邏輯
        BatchResult batchResult = new BatchResult(ms, boundSql, parameter);
        batchResults.add(batchResult);
        return 1; // 返回值僅表示操作成功,不是真實更新條數(shù)
    }

    @Override
    public List<BatchResult> flushStatements() throws SQLException {
        List<BatchResult> results = new ArrayList<>();
        for (PreparedStatement ps : statementMap.values()) {
            int[] updateCounts = ps.executeBatch(); // 批量提交
            // 將updateCounts記錄到BatchResult
        }
        statementMap.clear();
        batchResults.clear();
        return results;
    }
}

核心字段與方法:

  • batchResults:保存每條SQL的執(zhí)行結(jié)果,支持事務回滾。
  • statementMap:緩存PreparedStatement對象,實現(xiàn)SQL復用。
  • doUpdate:將SQL和參數(shù)加入批處理隊列,通過addBatch()延遲提交。
  • flushStatements:真正執(zhí)行批處理,通過executeBatch()一次性提交所有SQL。

5.3 doUpdate與flushStatements源碼解析

doUpdate方法:

@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    String sql = boundSql.getSql();
    PreparedStatement ps = statementMap.get(sql);
    if (ps == null) {
        Connection connection = getConnection();
        ps = connection.prepareStatement(sql);
        statementMap.put(sql, ps);
    }
    parameterHandler.setParameters(ps);
    ps.addBatch(); // 將參數(shù)加入JDBC批處理
    batchResults.add(new BatchResult(ms, boundSql, parameter));
    return 1;
}

逐行解析

  • 獲取SQL和參數(shù)綁定對象(BoundSql)。
  • 嘗試復用已有PreparedStatement,若不存在則創(chuàng)建。
  • 綁定參數(shù)(ParameterHandler)。
  • 關(guān)鍵點ps.addBatch()將SQL及參數(shù)緩存到JDBC批處理中,不立即執(zhí)行。
  • 記錄BatchResult以便事務回滾或結(jié)果統(tǒng)計。

flushStatements方法:

@Override
public List<BatchResult> flushStatements() throws SQLException {
    List<BatchResult> results = new ArrayList<>();
    for (Map.Entry<String, PreparedStatement> entry : statementMap.entrySet()) {
        PreparedStatement ps = entry.getValue();
        int[] updateCounts = ps.executeBatch(); // 批量提交
        // 將updateCounts記錄到BatchResult
        results.addAll(batchResults);
    }
    statementMap.clear();
    batchResults.clear();
    return results;
}

逐行解析

  • 遍歷緩存的PreparedStatement對象。
  • 關(guān)鍵點executeBatch()一次性提交所有SQL,大幅減少網(wǎng)絡往返和數(shù)據(jù)庫解析開銷。
  • 清空緩存,為下一輪批處理準備。
  • 返回批處理結(jié)果列表,支持事務管理。

5.4 BatchExecutor配置示例

全局配置(mybatis-config.xml):

<configuration>
    <settings>
        <setting name="defaultExecutorType" value="BATCH"/>
    </settings>
</configuration>

動態(tài)創(chuàng)建BatchExecutor:

try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    for (int i = 0; i < 1000; i++) {
        mapper.insertUser(new User(i, "User" + i));
    }
    sqlSession.commit(); // 批量提交
}

特點:所有SQL通過addBatch緩存,commit或flushStatements時一次性提交數(shù)據(jù)庫。

5.5 性能對比實驗

實驗場景:插入1000條用戶記錄,比較SimpleExecutor、ReuseExecutor、BatchExecutor性能。

long start = System.currentTimeMillis();
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
    UserMapper mapper = session.getMapper(UserMapper.class);
    for (int i = 0; i < 1000; i++) {
        mapper.insertUser(new User(i, "User" + i));
    }
    session.commit();
}
long end = System.currentTimeMillis();
System.out.println("BatchExecutor 耗時: " + (end - start) + "ms");

實驗結(jié)果示例(基于MySQL + MyBatis 3.5.9):

Executor類型1000條插入耗時優(yōu)勢
SimpleExecutor350ms實現(xiàn)簡單,每條SQL單獨提交
ReuseExecutor180ms復用Statement,減少創(chuàng)建開銷
BatchExecutor40msJDBC批量提交,性能最高

小結(jié):BatchExecutor是處理大量插入/更新的首選執(zhí)行器,可顯著減少數(shù)據(jù)庫交互次數(shù)。

5.6 使用注意事項

  • 事務范圍:批處理操作應在事務內(nèi)執(zhí)行,事務提交時才真正寫入數(shù)據(jù)庫。
  • 緩存Statement:BatchExecutor內(nèi)部緩存PreparedStatement,避免重復創(chuàng)建。
  • 適用場景:適合大批量數(shù)據(jù)寫入,但不適合頻繁查詢操作。
  • 返回值doUpdate()返回值不是真實更新條數(shù),需通過BatchResult獲取。
  • 數(shù)據(jù)庫兼容性:部分數(shù)據(jù)庫驅(qū)動對executeBatch()的支持不同,需要測試。

5.7 小結(jié)

  • 核心價值:批量提交SQL,大幅減少數(shù)據(jù)庫往返次數(shù)和JDBC開銷。
  • 與ReuseExecutor區(qū)別:ReuseExecutor只復用Statement,BatchExecutor才是真正的JDBC批處理。
  • 性能優(yōu)勢:適合大數(shù)據(jù)量寫入/更新場景,能顯著降低執(zhí)行時間。
  • 事務控制:批處理操作依賴事務管理,保證數(shù)據(jù)一致性。

6. CachingExecutor與緩存體系

CachingExecutor是MyBatis中用于一級緩存和二級緩存的核心執(zhí)行器,它通過裝飾模式(Decorator)封裝其他Executor,實現(xiàn)對查詢結(jié)果的緩存管理,從而減少數(shù)據(jù)庫訪問,提高查詢性能。

6.1 CachingExecutor的設計理念與作用

設計理念

MyBatis采用裝飾器模式將緩存邏輯與具體執(zhí)行器(SimpleExecutor、BatchExecutor等)分離,保證緩存功能可插拔。

CachingExecutor在執(zhí)行query方法時,先查詢緩存;若緩存命中,則直接返回結(jié)果,否則調(diào)用底層Executor執(zhí)行數(shù)據(jù)庫操作,并將結(jié)果放入緩存。

作用

  • 一級緩存(Session級):默認啟用,作用域為SqlSession,同一SqlSession中重復查詢可命中緩存。
  • 二級緩存(Mapper級/Namespace級):可選啟用,作用域為Mapper對應的Namespace,多個SqlSession共享緩存。
  • 緩存透明化:開發(fā)者無需關(guān)心底層Executor類型,直接通過Mapper查詢即可享受緩存優(yōu)化。

注意:二級緩存需要手動配置,且支持不同緩存實現(xiàn)(如PerpetualCacheEhcache)。

6.2 CachingExecutor源碼結(jié)構(gòu)

public class CachingExecutor implements Executor {
    private final Executor delegate;
    private final TransactionalCacheManager tcm = new TransactionalCacheManager();

    public CachingExecutor(Executor delegate) {
        this.delegate = delegate;
    }

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        Cache cache = ms.getCache();
        if (cache != null) {
            flushCacheIfRequired(ms);
            CacheKey key = createCacheKey(ms, parameter, rowBounds, null);
            @SuppressWarnings("unchecked")
            List<E> list = (List<E>) tcm.getObject(cache, key);
            if (list == null) {
                list = delegate.query(ms, parameter, rowBounds, resultHandler);
                tcm.putObject(cache, key, list);
            }
            return list;
        } else {
            return delegate.query(ms, parameter, rowBounds, resultHandler);
        }
    }
}

核心字段與方法:

  • delegate:被裝飾的底層Executor(Simple/Batch/Reuse等)。
  • tcmTransactionalCacheManager,管理緩存的事務性提交和回滾。
  • query():緩存命中檢查與數(shù)據(jù)查詢的核心邏輯。
  • flushCacheIfRequired():判斷是否需要刷新緩存(如更新操作會觸發(fā)清空相關(guān)緩存)。

6.3 一級緩存(Local Cache)機制

概念

  • 一級緩存是SqlSession級別緩存,每個SqlSession內(nèi)部獨立。

  • 默認開啟,不需要額外配置。

工作流程

  • 調(diào)用CachingExecutor.query()時生成CacheKey。
  • 檢查TransactionalCacheManager中的一級緩存是否存在對應Key。
  • 若命中,直接返回緩存結(jié)果。
  • 若未命中,調(diào)用底層Executor查詢數(shù)據(jù)庫,并將結(jié)果放入一級緩存。

CacheKey生成規(guī)則

  • MappedStatement ID
  • SQL文本
  • 參數(shù)對象
  • RowBounds偏移量
  • Environment ID(多環(huán)境區(qū)分)

6.4 二級緩存(Namespace Cache)機制

概念

二級緩存是Mapper級別緩存,多個SqlSession共享。

需要在Mapper XML中配置開啟:

<cache
    type="org.apache.ibatis.cache.impl.PerpetualCache"
    eviction="LRU"
    flushInterval="60000"
    size="512"
    readOnly="true"/>

特點

  • 可配置緩存實現(xiàn)(如PerpetualCache、Ehcache)。
  • 可設置淘汰策略(LRU、FIFO等)、刷新間隔、緩存大小。
  • 支持只讀或可更新模式(readOnly=true時返回對象的深拷貝,保證緩存安全)。

工作流程

  • 查詢時,CachingExecutor先查詢一級緩存。
  • 一級緩存未命中,查詢二級緩存。
  • 二級緩存未命中,調(diào)用底層Executor查詢數(shù)據(jù)庫。
  • 查詢結(jié)果先寫入二級緩存,提交事務后更新一級緩存。

核心邏輯由TransactionalCacheManager實現(xiàn)事務性緩存,保證更新操作回滾時不會污染緩存。

6.5 CachingExecutor與Executor協(xié)作關(guān)系

CachingExecutor
    └──> delegate (SimpleExecutor / ReuseExecutor / BatchExecutor)
           └──> JDBC操作

協(xié)作說明

  • 查詢操作:CachingExecutor檢查緩存 -> delegate執(zhí)行SQL -> 結(jié)果回填緩存。
  • 更新操作:CachingExecutor觸發(fā)緩存刷新 -> delegate執(zhí)行更新 -> 事務提交時清理緩存。
  • 事務一致性:通過TransactionalCacheManager管理緩存事務邊界,保證一級/二級緩存與數(shù)據(jù)庫一致。

6.6 示例:開啟二級緩存與查詢緩存命中

Mapper XML示例:

<mapper namespace="com.example.mapper.UserMapper">
    <cache type="org.apache.ibatis.cache.impl.PerpetualCache"
           eviction="LRU"
           flushInterval="300000"
           size="1024"
           readOnly="true"/>
    
    <select id="getUserById" parameterType="int" resultType="User">
        SELECT id, name, age FROM user WHERE id = #{id}
    </select>
</mapper>

測試代碼:

try (SqlSession session1 = sqlSessionFactory.openSession()) {
    UserMapper mapper1 = session1.getMapper(UserMapper.class);
    User user1 = mapper1.getUserById(1); // 查詢數(shù)據(jù)庫
    User user2 = mapper1.getUserById(1); // 命中一級緩存
    session1.commit();
}

try (SqlSession session2 = sqlSessionFactory.openSession()) {
    UserMapper mapper2 = session2.getMapper(UserMapper.class);
    User user3 = mapper2.getUserById(1); // 命中二級緩存
}

結(jié)果說明:

  • session1中重復查詢命中一級緩存
  • session2查詢命中二級緩存

6.7 使用注意事項與優(yōu)化策略

  • 更新操作刷新緩存insert/update/delete會觸發(fā)相關(guān)緩存清理。
  • 緩存鍵唯一性:復雜查詢參數(shù)需確保CacheKey唯一,否則可能緩存污染。
  • 緩存粒度:二級緩存粒度為Mapper Namespace,注意跨Mapper數(shù)據(jù)更新可能導致緩存失效。
  • 只讀緩存優(yōu)化:設置readOnly=true減少對象深拷貝開銷。
  • 事務邊界管理:通過TransactionalCacheManager確保事務回滾時緩存一致性。

6.8 小結(jié)

  • CachingExecutor是裝飾器,增強了底層Executor的緩存能力。
  • 一級緩存:SqlSession級別,默認開啟。
  • 二級緩存:Mapper級別,需要顯式配置,支持多種實現(xiàn)策略。
  • 事務性管理:保證緩存與數(shù)據(jù)庫一致性。
  • 性能優(yōu)化:適用于高頻查詢場景,可大幅降低數(shù)據(jù)庫訪問壓力。

7. Executor選型與性能調(diào)優(yōu)

MyBatis中Executor的類型主要包括:SimpleExecutor、ReuseExecutor、BatchExecutor以及緩存增強的CachingExecutor。

不同執(zhí)行器在數(shù)據(jù)庫交互次數(shù)、事務管理、緩存支持、批量操作性能上存在顯著差異。合理選型對系統(tǒng)性能優(yōu)化至關(guān)重要。

7.1 Executor性能對比概覽

Executor 類型特點優(yōu)點缺點適用場景
SimpleExecutor每次執(zhí)行SQL都創(chuàng)建Statement實現(xiàn)簡單,適合單條操作Statement重復創(chuàng)建,開銷大單條操作,低并發(fā)
ReuseExecutor復用Statement減少Statement創(chuàng)建次數(shù),提高性能不支持批量操作多次相同SQL操作
BatchExecutor批處理SQL批量插入/更新性能高對單條操作無優(yōu)勢,調(diào)試復雜批量數(shù)據(jù)寫入
CachingExecutor緩存封裝Executor減少重復查詢,提高查詢效率占用內(nèi)存,緩存更新需謹慎高頻查詢、讀多寫少場景

注意:CachingExecutor通常與其他Executor結(jié)合使用,如CachingExecutor(SimpleExecutor),因此在性能分析時需要考慮緩存命中率。

7.2 Executor選型策略

7.2.1 單條操作(插入/更新/刪除)

推薦Executor:SimpleExecutor

理由

  • 操作簡單,Statement創(chuàng)建開銷在單條操作下可接受
  • 易于調(diào)試和事務管理

7.2.2 重復執(zhí)行相同SQL

推薦Executor:ReuseExecutor

理由

  • 復用Statement,減少預編譯開銷
  • 對小批量重復操作性能提升明顯

示例配置

<configuration>
  <settings>
    <setting name="defaultExecutorType" value="REUSE"/>
  </settings>
</configuration>

7.2.3 批量數(shù)據(jù)寫入

推薦Executor:BatchExecutor

理由

  • 使用JDBC批處理(addBatch + executeBatch),顯著減少網(wǎng)絡交互次數(shù)
  • 對大數(shù)據(jù)量插入/更新性能優(yōu)化明顯

示例代碼

try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
    UserMapper mapper = session.getMapper(UserMapper.class);
    for (int i = 0; i < 1000; i++) {
        mapper.insertUser(new User("User" + i, 20 + i % 30));
    }
    session.commit(); // 觸發(fā)批量執(zhí)行
}

7.3 性能測試實驗:SimpleExecutor vs BatchExecutor

實驗場景

  • 插入1000條用戶數(shù)據(jù)
  • 測試兩種Executor的耗時

測試代碼

public void testInsertPerformance() {
    int count = 1000;

    // SimpleExecutor
    long startSimple = System.currentTimeMillis();
    try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.SIMPLE)) {
        UserMapper mapper = session.getMapper(UserMapper.class);
        for (int i = 0; i < count; i++) {
            mapper.insertUser(new User("User" + i, 20 + i % 30));
        }
        session.commit();
    }
    long endSimple = System.currentTimeMillis();

    // BatchExecutor
    long startBatch = System.currentTimeMillis();
    try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
        UserMapper mapper = session.getMapper(UserMapper.class);
        for (int i = 0; i < count; i++) {
            mapper.insertUser(new User("User" + i, 20 + i % 30));
        }
        session.commit();
    }
    long endBatch = System.currentTimeMillis();

    System.out.println("SimpleExecutor耗時:" + (endSimple - startSimple) + " ms");
    System.out.println("BatchExecutor耗時:" + (endBatch - startBatch) + " ms");
}

實驗結(jié)果示意

SimpleExecutor耗時:1200 ms
BatchExecutor耗時:180 ms

結(jié)論:

  • BatchExecutor在大批量操作下性能提升顯著(約7倍)
  • 小批量或單條操作時BatchExecutor優(yōu)勢不明顯,SimpleExecutor或ReuseExecutor更適合

7.4 Executor與事務管理

事務邊界SqlSession控制:commit/rollback

BatchExecutor注意事項

  • 批量SQL在事務提交時統(tǒng)一發(fā)送到數(shù)據(jù)庫
  • 回滾操作會取消整個批次執(zhí)行

緩存Executor與事務結(jié)合:

  • 一級緩存自動在事務范圍內(nèi)生效
  • 二級緩存由TransactionalCacheManager管理,保證事務一致性

7.5 高級優(yōu)化建議

組合Executor與緩存

  • 高頻查詢 + 重復SQL:CachingExecutor(ReuseExecutor)
  • 批量寫入 + 查詢緩存:CachingExecutor(BatchExecutor)

批量操作分批提交

  • 避免一次提交過多,導致JDBC內(nèi)存壓力大
  • 推薦每500~1000條分批提交

監(jiān)控SQL執(zhí)行時間

  • 使用MyBatis插件(Interceptor)監(jiān)控Statement執(zhí)行時間
  • 動態(tài)調(diào)整Executor策略

緩存更新策略

  • 對寫多讀少場景可降低緩存依賴
  • 對讀多寫少場景,啟用二級緩存顯著提升性能

7.6 場景化選型總結(jié)

場景推薦Executor備注
單條查詢/更新SimpleExecutor調(diào)試方便
重復查詢同SQLReuseExecutor減少Statement創(chuàng)建
大量批量插入/更新BatchExecutor使用JDBC批處理優(yōu)化
高頻查詢CachingExecutor + ReuseExecutor一級/二級緩存加速查詢
批量更新且需緩存CachingExecutor + BatchExecutor保證緩存一致性

7.7 小結(jié)

  • Executor選型應結(jié)合操作類型和數(shù)據(jù)量,單條操作不宜使用批處理Executor,大批量操作推薦BatchExecutor。
  • 緩存Executor能顯著提升查詢性能,尤其在讀多寫少場景。
  • 事務邊界和緩存一致性管理是優(yōu)化性能和保證數(shù)據(jù)正確性的關(guān)鍵。
  • 結(jié)合實際開發(fā)需求,合理選擇Executor類型,并配合緩存和事務策略,能實現(xiàn)MyBatis性能優(yōu)化的最大化。

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java中List與數(shù)組之間的相互轉(zhuǎn)換

    Java中List與數(shù)組之間的相互轉(zhuǎn)換

    在日常Java學習或項目開發(fā)中,經(jīng)常會遇到需要int[]數(shù)組和List列表相互轉(zhuǎn)換的場景,然而往往一時難以想到有哪些方法,最后可能會使用暴力逐個轉(zhuǎn)換法,往往不是我們所滿意的,下面這篇文章主要給大家介紹了關(guān)于Java中List與數(shù)組之間的相互轉(zhuǎn)換,需要的朋友可以參考下
    2023-05-05
  • Java垃圾回收機制的finalize方法實例分析

    Java垃圾回收機制的finalize方法實例分析

    這篇文章主要介紹了Java垃圾回收機制的finalize方法,結(jié)合實例形式分析了finalize方法的特點及在垃圾回收機制中的相關(guān)操作技巧,需要的朋友可以參考下
    2019-08-08
  • Mybatis之Select Count(*)的獲取返回int的值操作

    Mybatis之Select Count(*)的獲取返回int的值操作

    這篇文章主要介紹了Mybatis之Select Count(*)的獲取返回int的值操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • idea安裝與配置及基本用法教程詳解

    idea安裝與配置及基本用法教程詳解

    這篇文章主要介紹了idea安裝與配置及基本用法教程詳解,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03
  • java動態(tài)加載插件化編程詳解

    java動態(tài)加載插件化編程詳解

    這篇文章主要介紹了java動態(tài)加載插件化編程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-07-07
  • Java Web開發(fā)之基于Session的購物商店實現(xiàn)方法

    Java Web開發(fā)之基于Session的購物商店實現(xiàn)方法

    這篇文章主要介紹了Java Web開發(fā)之基于Session的購物商店實現(xiàn)方法,涉及Java針對session的操作及數(shù)據(jù)庫操作技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-10-10
  • SpringBoot配置diff的實戰(zhàn)指南

    SpringBoot配置diff的實戰(zhàn)指南

    做配置管理時,diff 是繞不開的功能,比如運維要修改生產(chǎn)環(huán)境配置,改之前總想確認一下具體動了什么,通常我們會想到用 Unix 的 diff 命令或者 Git 的 diff 功能,所以本文給大家介紹了SpringBoot配置diff的實戰(zhàn)指南,需要的朋友可以參考下
    2026-01-01
  • java 優(yōu)雅關(guān)閉線程池的方案

    java 優(yōu)雅關(guān)閉線程池的方案

    這篇文章主要介紹了java 優(yōu)雅關(guān)閉線程池的方案,幫助大家更好的理解和學習Java,感興趣的朋友可以了解下
    2020-11-11
  • 這一次搞懂Spring的XML解析原理說明

    這一次搞懂Spring的XML解析原理說明

    這篇文章主要介紹了這一次搞懂Spring的XML解析原理說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • java Spring松耦合高效應用簡單實例分析

    java Spring松耦合高效應用簡單實例分析

    在Java項目,龐大的對象依賴關(guān)系將一直緊密耦合引起對象難以管理或修改。在這種情況下,可以使用Spring框架作為一個核心模塊輕松高效地管理所有的對象依賴。本文章向大家介紹Spring松耦合的實例,需要的朋友可以參考一下。
    2016-12-12

最新評論

垦利县| 海晏县| 绿春县| 灵武市| 曲水县| 常德市| 乃东县| 湘乡市| 汨罗市| 聊城市| 句容市| 苍山县| 苏尼特右旗| 体育| 高邮市| 望江县| 荣成市| 上饶县| 清远市| 宁阳县| 泽州县| 华池县| 呼伦贝尔市| 巴马| 高陵县| 楚雄市| 勐海县| 海阳市| 固阳县| 夹江县| 长垣县| 通辽市| 内江市| 玉山县| 新安县| 大厂| 正安县| 济南市| 陇南市| 锡林郭勒盟| 临高县|