MyBatis使用CASE WHEN進(jìn)行批量更新的高效寫法
在實(shí)際業(yè)務(wù)開發(fā)中,我們經(jīng)常需要 批量更新多條數(shù)據(jù)。
最常見的做法是:
for (Item item : list) {
mapper.update(item);
}
但這樣會執(zhí)行多次 SQL,性能低下,尤其當(dāng)更新上百條數(shù)據(jù)時(shí),數(shù)據(jù)庫壓力很大。
有沒有更高效的方式?
有!可以使用 SQL 的 CASE WHEN 批量更新寫法。
一、為什么要用 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ù)庫交互次數(shù);
- 兼容 MyBatis 動(dòng)態(tài) SQL,非常靈活。
二、示例場景
假設(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 批量更新對應(yīng)的 product_id 和 warehouse_id。
三、MyBatis 批量更新寫法(CASE WHEN)
以下是完整的 MyBatis XML 寫法:
<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 (...)
可能會問:這不是多余的嗎?
其實(shí)它是 必須的!
沒有這個(gè)條件,數(shù)據(jù)庫會對整張表執(zhí)行 UPDATE,
未命中的記錄的 CASE WHEN 結(jié)果是 NULL,導(dǎo)致字段被更新成 NULL,數(shù)據(jù)直接“報(bào)廢”。 ??
所以必須加上 WHERE identity_code IN (...) 來限制更新范圍。
六、性能對比
| 更新方式 | SQL 執(zhí)行次數(shù) | 性能(1000 條) | 備注 |
|---|---|---|---|
| 循環(huán)單條更新 | 1000 次 | ?? 慢 | 每次一條 SQL |
| MyBatis 批量更新(CASE WHEN) | 1 次 | ?? 快 | 一次 SQL 搞定 |
| MyBatis 批量更新(foreach 多值) | 中等 | ? 一般 | 取決于實(shí)現(xiàn)邏輯 |
推薦場景:
當(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 長度 | MySQL 默認(rèn)最大 1MB,可通過 max_allowed_packet 調(diào)整 |
| 時(shí)間字段 | 可以統(tǒng)一 update_time = NOW() |
| 數(shù)據(jù)庫 | 適用于 MySQL、PostgreSQL、SQL Server |
九、總結(jié)
| 優(yōu)點(diǎn) | 缺點(diǎn) |
|---|---|
| 批量更新性能高 | SQL 長度可能受限 |
| 一次執(zhí)行減少數(shù)據(jù)庫壓力 | 可讀性稍弱 |
| 兼容 MyBatis 動(dòng)態(tài) SQL | 僅適合根據(jù)主鍵或唯一字段更新 |
結(jié)語
CASE WHEN 批量更新寫法,是 MyBatis 開發(fā)中非常實(shí)用的一種性能優(yōu)化技巧。
在日常項(xiàng)目中,當(dāng)需要 根據(jù)不同條件批量更新不同字段值 時(shí),它幾乎是最優(yōu)方案。
一條 SQL,解決多條更新,讓你的系統(tǒng)更快、更穩(wěn)、更優(yōu)雅。
以上就是MyBatis使用CASE WHEN進(jìn)行批量更新的高效寫法的詳細(xì)內(nèi)容,更多關(guān)于MyBatis CASE WHEN批量更新的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
淺談SpringBoot實(shí)現(xiàn)自動(dòng)裝配的方法原理
SpringBoot的自動(dòng)裝配是它的一大特點(diǎn),可以大大提高開發(fā)效率,減少重復(fù)性代碼的編寫。本文將詳細(xì)講解SpringBoot如何實(shí)現(xiàn)自動(dòng)裝配,需要的朋友可以參考下2023-05-05
使用SpringCache操作Redis緩存數(shù)據(jù)的示例代碼
SpringCache是一個(gè)框架,實(shí)現(xiàn)了基于注解的緩存功能,只需要簡單的加一個(gè)注解,就能實(shí)現(xiàn)緩存功能,本文給大家介紹了如何使用SpringCache操作Redis緩存數(shù)據(jù),文中有相關(guān)的代碼示例供大家參考,需要的朋友可以參考下2024-01-01
Spring實(shí)戰(zhàn)之使用TransactionProxyFactoryBean實(shí)現(xiàn)聲明式事務(wù)操作示例
這篇文章主要介紹了Spring實(shí)戰(zhàn)之使用TransactionProxyFactoryBean實(shí)現(xiàn)聲明式事務(wù)操作,結(jié)合實(shí)例形式分析了spring使用TransactionProxyFactoryBean實(shí)現(xiàn)聲明式事務(wù)相關(guān)配置、接口設(shè)置與使用技巧,需要的朋友可以參考下2020-01-01
一文帶你了解微服務(wù)架構(gòu)中的"發(fā)件箱模式"
微服務(wù)架構(gòu)如今非常的流行,這個(gè)架構(gòu)下可能經(jīng)常會遇到“雙寫”的場景。本文就和大家分享一個(gè)“發(fā)件箱模式”,?感興趣的小伙伴可以了解一下2023-01-01
java根據(jù)當(dāng)前時(shí)間獲取yyyy-MM-dd?HH:mm:ss標(biāo)準(zhǔn)格式的時(shí)間代碼示例
在Java中可以使用java.time包中的LocalDateTime類和DateTimeFormatter類來獲取并格式化當(dāng)前時(shí)間為yyyy-MM-dd?HH:mm:ss的格式,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-10-10
Java實(shí)現(xiàn)鎖定某個(gè)變量的幾種方式示例詳解
這篇文章主要為大家介紹了Java實(shí)現(xiàn)鎖某個(gè)變量的幾種方式示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
java byte與base64的互轉(zhuǎn)的實(shí)現(xiàn)示例
在項(xiàng)目開發(fā)中經(jīng)常用到,比如前端上送文件流(byte[])到后臺并轉(zhuǎn)成文件,本文主要介紹了java byte與base64的互轉(zhuǎn)的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02

