MyBatis使用CASE WHEN進(jìn)行批量更新的高效寫(xiě)法
在實(shí)際業(yè)務(wù)開(kāi)發(fā)中,我們經(jīng)常需要 批量更新多條數(shù)據(jù)。
最常見(jiàn)的做法是:
for (Item item : list) {
mapper.update(item);
}
但這樣會(huì)執(zhí)行多次 SQL,性能低下,尤其當(dāng)更新上百條數(shù)據(jù)時(shí),數(shù)據(jù)庫(kù)壓力很大。
有沒(méi)有更高效的方式?
有!可以使用 SQL 的 CASE WHEN 批量更新寫(xiě)法。
一、為什么要用 CASE WHEN 批量更新?
傳統(tǒng)循環(huán)更新的缺點(diǎn):
- 每次執(zhí)行一條
UPDATE,頻繁網(wǎng)絡(luò) IO。 - MyBatis 多次 prepare statement,效率低。
- 數(shù)據(jù)量大時(shí),明顯拖慢系統(tǒng)性能。
而 CASE WHEN 批量更新的優(yōu)點(diǎn):
- 一次 SQL 完成多條更新;
- 性能提升明顯(尤其在上百條數(shù)據(jù)時(shí));
- 減少數(shù)據(jù)庫(kù)交互次數(shù);
- 兼容 MyBatis 動(dòng)態(tài) SQL,非常靈活。
二、示例場(chǎng)景
假設(shè)我們有一張表:
CREATE TABLE erp_product_identity ( id BIGINT PRIMARY KEY, identity_code VARCHAR(50) UNIQUE, product_id BIGINT, warehouse_id BIGINT, update_time DATETIME );
我們現(xiàn)在有一個(gè) list,需要根據(jù) identity_code 批量更新對(duì)應(yīng)的 product_id 和 warehouse_id。
三、MyBatis 批量更新寫(xiě)法(CASE WHEN)
以下是完整的 MyBatis XML 寫(xiě)法:
<update id="batchUpdateProductIdentityCase">
UPDATE erp_product_identity
<set>
product_id = CASE identity_code
<foreach collection="list" item="item">
WHEN #{item.identityCode} THEN #{item.productId}
</foreach>
END,
warehouse_id = CASE identity_code
<foreach collection="list" item="item">
WHEN #{item.identityCode} THEN #{item.warehouseId}
</foreach>
END,
update_time = NOW()
</set>
WHERE identity_code IN
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item.identityCode}
</foreach>
</update>
四、SQL 生成效果展示
當(dāng)傳入 3 條記錄時(shí):
list = [
{ identityCode: 'A001', productId: 11, warehouseId: 101 },
{ identityCode: 'A002', productId: 22, warehouseId: 102 },
{ identityCode: 'A003', productId: 33, warehouseId: 103 }
];
最終生成的 SQL 如下:
UPDATE erp_product_identity
SET
product_id = CASE identity_code
WHEN 'A001' THEN 11
WHEN 'A002' THEN 22
WHEN 'A003' THEN 33
END,
warehouse_id = CASE identity_code
WHEN 'A001' THEN 101
WHEN 'A002' THEN 102
WHEN 'A003' THEN 103
END,
update_time = NOW()
WHERE identity_code IN ('A001', 'A002', 'A003');
一次 SQL 更新三條數(shù)據(jù)。
效率遠(yuǎn)高于逐條執(zhí)行!
五、WHERE IN 的作用(非常關(guān)鍵?。?/h2>
有些人看到這行:
WHERE identity_code IN (...)
可能會(huì)問(wèn):這不是多余的嗎?
其實(shí)它是 必須的!
沒(méi)有這個(gè)條件,數(shù)據(jù)庫(kù)會(huì)對(duì)整張表執(zhí)行 UPDATE,
未命中的記錄的 CASE WHEN 結(jié)果是 NULL,導(dǎo)致字段被更新成 NULL,數(shù)據(jù)直接“報(bào)廢”。 ??
所以必須加上 WHERE identity_code IN (...) 來(lái)限制更新范圍。
六、性能對(duì)比
| 更新方式 | SQL 執(zhí)行次數(shù) | 性能(1000 條) | 備注 |
|---|---|---|---|
| 循環(huán)單條更新 | 1000 次 | ?? 慢 | 每次一條 SQL |
| MyBatis 批量更新(CASE WHEN) | 1 次 | ?? 快 | 一次 SQL 搞定 |
| MyBatis 批量更新(foreach 多值) | 中等 | ? 一般 | 取決于實(shí)現(xiàn)邏輯 |
推薦場(chǎng)景:
當(dāng)你要批量更新幾十到幾百條數(shù)據(jù)(上千條以內(nèi))時(shí),CASE WHEN 是最優(yōu)解。
七、可維護(hù)性優(yōu)化方案
為了可讀性更高,可以把 CASE 部分封裝成單獨(dú) SQL 片段:
<sql id="caseProductId">
product_id = CASE identity_code
<foreach collection="list" item="item">
WHEN #{item.identityCode} THEN #{item.productId}
</foreach>
END
</sql>
<sql id="caseWarehouseId">
warehouse_id = CASE identity_code
<foreach collection="list" item="item">
WHEN #{item.identityCode} THEN #{item.warehouseId}
</foreach>
END
</sql>
<update id="batchUpdateProductIdentityCase">
UPDATE erp_product_identity
<set>
<include refid="caseProductId"/>
,
<include refid="caseWarehouseId"/>
,
update_time = NOW()
</set>
WHERE identity_code IN
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item.identityCode}
</foreach>
</update>
這樣結(jié)構(gòu)更清晰、維護(hù)更方便。
八、使用建議與限制
| 項(xiàng)目 | 建議 / 限制 |
|---|---|
identity_code | 必須是唯一鍵或主鍵 |
| 批量數(shù)量 | 推薦單次 ≤ 1000 條 |
| SQL 長(zhǎng)度 | MySQL 默認(rèn)最大 1MB,可通過(guò) max_allowed_packet 調(diào)整 |
| 時(shí)間字段 | 可以統(tǒng)一 update_time = NOW() |
| 數(shù)據(jù)庫(kù) | 適用于 MySQL、PostgreSQL、SQL Server |
九、總結(jié)
| 優(yōu)點(diǎn) | 缺點(diǎn) |
|---|---|
| 批量更新性能高 | SQL 長(zhǎng)度可能受限 |
| 一次執(zhí)行減少數(shù)據(jù)庫(kù)壓力 | 可讀性稍弱 |
| 兼容 MyBatis 動(dòng)態(tài) SQL | 僅適合根據(jù)主鍵或唯一字段更新 |
結(jié)語(yǔ)
CASE WHEN 批量更新寫(xiě)法,是 MyBatis 開(kāi)發(fā)中非常實(shí)用的一種性能優(yōu)化技巧。
在日常項(xiàng)目中,當(dāng)需要 根據(jù)不同條件批量更新不同字段值 時(shí),它幾乎是最優(yōu)方案。
一條 SQL,解決多條更新,讓你的系統(tǒng)更快、更穩(wěn)、更優(yōu)雅。
以上就是MyBatis使用CASE WHEN進(jìn)行批量更新的高效寫(xiě)法的詳細(xì)內(nèi)容,更多關(guān)于MyBatis CASE WHEN批量更新的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Springboot應(yīng)用中@EntityScan和@EnableJpaRepositories的使用詳解
在Spring Boot中,若Entity和Repository不在主包內(nèi),需通過(guò)@EntityScan和@EnableJpaRepositories指定掃描路徑,注意basePackages需完整覆蓋,避免重復(fù)注冊(cè)或Bean缺失錯(cuò)誤2025-08-08
JavaGUI界面實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)方法
這篇文章主要給大家介紹了關(guān)于JavaGUI界面實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)的相關(guān)資料, GUI是指圖形用戶界面,指采用圖形方式顯示的計(jì)算機(jī)操作用戶界面,需要的朋友可以參考下2023-07-07
SpringBoot項(xiàng)目中@Test不出現(xiàn)可點(diǎn)擊運(yùn)行的按鈕問(wèn)題
這篇文章主要介紹了SpringBoot項(xiàng)目中@Test不出現(xiàn)可點(diǎn)擊運(yùn)行的按鈕問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
一篇文章帶你搞懂Java restful 接口開(kāi)發(fā)
這篇文章主要介紹了Java restful 接口開(kāi)發(fā)的幾種方式(HTTPS),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2021-10-10
詳解SpringCloudGateway內(nèi)存泄漏問(wèn)題
這篇文章主要介紹了詳解SpringCloudGateway內(nèi)存泄漏問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
Java編程實(shí)現(xiàn)快速排序及優(yōu)化代碼詳解
這篇文章主要介紹了Java編程實(shí)現(xiàn)快速排序及優(yōu)化代碼詳解,具有一定借鑒價(jià)值,需要的朋友可以了解下。2017-12-12

