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

Spring Framework中JDBC批量操作的三種實(shí)現(xiàn)方式

 更新時(shí)間:2025年10月30日 09:06:31   作者:lang20150928  
本文詳細(xì)介紹了如何使用 Spring 的 JdbcTemplate 進(jìn)行高效的數(shù)據(jù)庫批量更新,從而減少與數(shù)據(jù)庫之間的網(wǎng)絡(luò)往返次數(shù)(round-trips),提升性能,下面我將用通俗易懂的方式,結(jié)合代碼示例和實(shí)際場景給大家詳細(xì)說說,需要的朋友可以參考下

這篇文章詳細(xì)介紹了如何使用 Spring 的 JdbcTemplate 進(jìn)行高效的數(shù)據(jù)庫批量更新,從而減少與數(shù)據(jù)庫之間的網(wǎng)絡(luò)往返次數(shù)(round-trips),提升性能。

下面我將用通俗易懂的方式,結(jié)合代碼示例和實(shí)際場景,幫你系統(tǒng)地理解這一節(jié)的核心思想和三種主要批量處理方式。

為什么要用“批量操作”?

在沒有批量處理時(shí),如果你要插入或更新 1000 條數(shù)據(jù):

for (Actor actor : actors) {
    jdbcTemplate.update("INSERT INTO t_actor ...", actor.getName(), ...);
}

這會(huì)向數(shù)據(jù)庫發(fā)送 1000 次獨(dú)立的 SQL 請(qǐng)求 → 1000 次網(wǎng)絡(luò)通信 → 效率極低。

而使用 批量操作(Batch Operations),你可以把多個(gè) SQL 操作“打包”成一組,一次性提交給數(shù)據(jù)庫執(zhí)行:

? 結(jié)果:

  • 減少網(wǎng)絡(luò)開銷
  • 提高吞吐量(可能提升幾十倍)
  • 更高效地利用數(shù)據(jù)庫資源

基本批量操作:使用 BatchPreparedStatementSetter

這是最傳統(tǒng)、控制最精細(xì)的方式。

使用方法:

實(shí)現(xiàn) BatchPreparedStatementSetter 接口的兩個(gè)方法:

  1. getBatchSize():返回本次批量操作的總條數(shù)
  2. setValues(PreparedStatement ps, int i):為第 i 條記錄設(shè)置參數(shù)

示例說明:

public int[] batchUpdate(final List<Actor> actors) {
    return this.jdbcTemplate.batchUpdate(
            "update t_actor set first_name = ?, last_name = ? where id = ?",
            new BatchPreparedStatementSetter() {
                public void setValues(PreparedStatement ps, int i) throws SQLException {
                    Actor actor = actors.get(i);
                    ps.setString(1, actor.getFirstName());
                    ps.setString(2, actor.getLastName());
                    ps.setLong(3, actor.getId().longValue());
                }
                public int getBatchSize() {
                    return actors.size(); // 整個(gè)列表作為一批
                }
            });
}

關(guān)鍵點(diǎn):

  • 整個(gè) actors 列表被當(dāng)作一個(gè)“大批次”一次性提交。
  • 返回值是 int[],每個(gè)元素表示對(duì)應(yīng) SQL 語句影響的行數(shù)。
  • 適用于:數(shù)據(jù)量不大且能一次性加載到內(nèi)存的情況。

特殊情況:流式輸入(不確定總數(shù))

如果數(shù)據(jù)來自文件、流或數(shù)據(jù)庫游標(biāo),無法預(yù)知總數(shù),可以使用:

InterruptibleBatchPreparedStatementSetter

它有一個(gè)額外方法:

boolean isBatchExhausted();

當(dāng)你讀完所有數(shù)據(jù)后返回 true,Spring 就停止繼續(xù)添加語句。

更簡潔的方式:用對(duì)象列表做批量操作

Spring 提供了更簡潔的 API —— 不用手動(dòng)實(shí)現(xiàn)接口,只需傳入一個(gè)對(duì)象列表!

方式一:使用命名參數(shù)(Named Parameters) + NamedParameterJdbcTemplate

public int[] batchUpdate(List<Actor> actors) {
    return this.namedParameterJdbcTemplate.batchUpdate(
        "update t_actor set first_name = :firstName, last_name = :lastName where id = :id",
        SqlParameterSourceUtils.createBatch(actors) // 自動(dòng)提取 getter
    );
}

優(yōu)點(diǎn):

  • 使用 :firstName 這種命名參數(shù),可讀性強(qiáng)
  • SqlParameterSourceUtils.createBatch(actors) 自動(dòng)從 Actor 對(duì)象的 getter 提取字段
  • 支持 POJO(JavaBean)、Map 等格式

要求:Actor 類必須有 getFirstName()、getId() 等標(biāo)準(zhǔn) getter 方法

方式二:使用 ? 占位符 + List<Object[]>

List<Object[]> batch = new ArrayList<>();
for (Actor actor : actors) {
    Object[] values = new Object[] {
        actor.getFirstName(), 
        actor.getLastName(), 
        actor.getId()
    };
    batch.add(values);
}

jdbcTemplate.batchUpdate(
    "update t_actor set first_name = ?, last_name = ? where id = ?", 
    batch
);

優(yōu)點(diǎn):

  • 簡單直觀
  • 不依賴命名參數(shù)

注意事項(xiàng):

  • 參數(shù)順序必須嚴(yán)格匹配 SQL 中的 ? 順序
  • 如果值為 null,Spring 需要通過反射推斷類型(可能影響性能)

性能提示:

Spring 默認(rèn)調(diào)用 ParameterMetaData.getParameterType() 來判斷 null 值的類型,但某些數(shù)據(jù)庫驅(qū)動(dòng)(如 Oracle 12c)這個(gè)操作很慢。
可以通過設(shè)置系統(tǒng)屬性關(guān)閉:

# 在 JVM 啟動(dòng)參數(shù)或 spring.properties 中添加
spring.jdbc.getParameterType.ignore=true

或者顯式指定類型(更推薦)。

處理超大數(shù)據(jù)集:分多個(gè)小批次提交(Multiple Batches)

前面的例子都是一次性提交全部數(shù)據(jù)。但如果數(shù)據(jù)量太大(比如 10 萬條),一次性加載到內(nèi)存會(huì)導(dǎo)致內(nèi)存溢出(OOM)。

解決方案:

使用分批提交(chunking),每 100 條提交一次。

public int[][] batchUpdate(final Collection<Actor> actors) {
    int[][] updateCounts = jdbcTemplate.batchUpdate(
        "update t_actor set first_name = ?, last_name = ? where id = ?",
        actors,              // 所有數(shù)據(jù)
        100,                 // 每批最多 100 條
        (ps, actor) -> {     // Lambda 設(shè)置參數(shù)
            ps.setString(1, actor.getFirstName());
            ps.setString(2, actor.getLastName());
            ps.setLong(3, actor.getId().longValue());
        }
    );
    return updateCounts;
}

返回值解釋:

int[][] updateCounts
  • 第一層數(shù)組:每個(gè)“批次”的結(jié)果
  • 第二層數(shù)組:該批次中每條 SQL 的影響行數(shù)

例如:

[
  [1, 1, 1, ..., 1],  // 第1批,共100條,每條影響1行
  [1, 1, 1, ..., 1],  // 第2批
  [1, 1, 1]           // 最后一批只有3條
]

適用場景:

  • 處理大量數(shù)據(jù)(數(shù)萬、數(shù)十萬條)
  • 防止內(nèi)存溢出
  • 支持進(jìn)度監(jiān)控、錯(cuò)誤恢復(fù)

三種方式對(duì)比總結(jié)

方式適用場景優(yōu)點(diǎn)缺點(diǎn)
BatchPreparedStatementSetter需要精確控制每條記錄參數(shù)靈活、支持中斷寫法較繁瑣
List<Object[]>SqlParameterSourceUtils.createBatch()數(shù)據(jù)已在內(nèi)存中,結(jié)構(gòu)簡單寫法簡潔不適合超大數(shù)據(jù)集
分批提交(multiple batches)超大數(shù)據(jù)集內(nèi)存友好、可控性強(qiáng)稍復(fù)雜

性能建議

合理設(shè)置批大小(batch size)

  • 太?。喝杂卸啻瓮?/li>
  • 太大:內(nèi)存壓力大,事務(wù)過長
  • 推薦值:50 ~ 1000(根據(jù)測試調(diào)整)

開啟數(shù)據(jù)庫的批量支持

  • MySQL:確保連接 URL 包含 rewriteBatchedStatements=true
jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
  • Oracle:原生支持較好
  • PostgreSQL:需要驅(qū)動(dòng)支持(reWriteBatchedInserts=true

使用連接池(如 HikariCP)

  • 批量操作期間保持連接穩(wěn)定

避免自動(dòng)提交(Auto-commit)

  • 在事務(wù)中執(zhí)行批量操作,防止中途失敗導(dǎo)致部分寫入

實(shí)際應(yīng)用建議(Spring Boot 項(xiàng)目)

如果你使用的是 Spring Boot,推薦寫法如下:

@Service
public class ActorService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void updateActorsInBatches(List<Actor> actors) {
        jdbcTemplate.batchUpdate(
            "UPDATE t_actor SET first_name = ?, last_name = ? WHERE id = ?",
            actors,
            100,
            (ps, actor) -> {
                ps.setString(1, actor.getFirstName());
                ps.setString(2, actor.getLastName());
                ps.setLong(3, actor.getId());
            }
        );
    }
}

搭配事務(wù)注解:

@Transactional
public void importHugeData() {
    List<Actor> hugeList = readFromCsvOrDatabase();
    updateActorsInBatches(hugeList); // 分批提交
}

總結(jié)一句話:

批量操作 = 把多條 SQL “打包”發(fā)送給數(shù)據(jù)庫,減少網(wǎng)絡(luò)來回,提高性能。Spring 提供了多種方式讓你既能簡單使用,也能精細(xì)控制。

下一步你可以思考:

  • 如何從 CSV 文件逐行讀取并分批導(dǎo)入?
  • 如何在批量操作中捕獲部分失敗的記錄?
  • 如何結(jié)合 @Async 實(shí)現(xiàn)并行批量處理?

以上就是Spring Framework中JDBC批量操作的三種實(shí)現(xiàn)方式的詳細(xì)內(nèi)容,更多關(guān)于Spring JDBC批量操作方式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java整合SSM框架的圖文教程

    java整合SSM框架的圖文教程

    下面筆者就為大家分享一篇java整合SSM框架的圖文教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨筆者過來看看吧
    2017-11-11
  • 認(rèn)識(shí)Java中的Stub與StubQueue

    認(rèn)識(shí)Java中的Stub與StubQueue

    StubQueue是用來保存生成的本地代碼的Stub隊(duì)列,隊(duì)列每一個(gè)元素對(duì)應(yīng)一個(gè)InterpreterCodelet對(duì)象,InterpreterCodelet對(duì)象繼承自抽象基類Stub,下面我們介紹一下StubQueue類及相關(guān)類Stub、InterpreterCodelet類和CodeletMark類。需要的的下伙伴可以參考下面文字內(nèi)容
    2021-09-09
  • 多個(gè)springboot項(xiàng)目如何使用一個(gè)外部共同的application.yml

    多個(gè)springboot項(xiàng)目如何使用一個(gè)外部共同的application.yml

    這篇文章主要介紹了多個(gè)springboot項(xiàng)目如何使用一個(gè)外部共同的application.yml問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • 深入理解Spring中的循環(huán)依賴

    深入理解Spring中的循環(huán)依賴

    Spring在注入bean的時(shí)候會(huì)做循環(huán)依賴檢查,下面這篇文章主要給大家介紹了關(guān)于Spring中循環(huán)依賴的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧
    2018-05-05
  • Java Maven高級(jí)之插件開發(fā)詳解

    Java Maven高級(jí)之插件開發(fā)詳解

    這篇文章主要介紹了Maven 插件開發(fā)的詳細(xì)整理的相關(guān)資料,需要的朋友可以看下,希望能夠給你帶來幫助
    2021-09-09
  • java生成圖片驗(yàn)證碼的示例代碼

    java生成圖片驗(yàn)證碼的示例代碼

    這篇文章主要介紹了java生成圖片驗(yàn)證碼的示例代碼,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-09-09
  • Springboot jdbctemplate整合實(shí)現(xiàn)步驟解析

    Springboot jdbctemplate整合實(shí)現(xiàn)步驟解析

    這篇文章主要介紹了Springboot jdbctemplate整合實(shí)現(xiàn)步驟解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Java?Map初始化并賦值的幾種簡短寫法

    Java?Map初始化并賦值的幾種簡短寫法

    在Java中初始化一個(gè)Map并賦值可以通過多種方式完成,下面這篇文章主要介紹了Java?Map初始化并賦值的幾種簡短寫法,文中給出了詳細(xì)的代碼示例,需要的朋友可以參考下
    2025-03-03
  • Java二叉樹查詢?cè)砩钊敕治鲋v解

    Java二叉樹查詢?cè)砩钊敕治鲋v解

    這篇文章主要介紹了Java二叉樹查詢?cè)?,二叉查找樹,又稱二叉排序樹,亦稱二叉搜索樹,是數(shù)據(jù)結(jié)構(gòu)中的一類。在一般情況下,查找效率比鏈表結(jié)構(gòu)要高
    2022-11-11
  • java基礎(chǔ)之接口組成更新的實(shí)現(xiàn)

    java基礎(chǔ)之接口組成更新的實(shí)現(xiàn)

    本文主要介紹了java基礎(chǔ)之接口組成更新的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04

最新評(píng)論

西华县| 彭水| 华宁县| 且末县| 巴楚县| 孝义市| 荔波县| 肃北| 长泰县| 兖州市| 墨竹工卡县| 灵宝市| 福安市| 洛宁县| 会昌县| 馆陶县| 阳泉市| 深圳市| 隆林| 武宣县| 乌兰浩特市| 安阳市| 皮山县| 商南县| 昌邑市| 明溪县| 兴安盟| 酒泉市| 福泉市| 五家渠市| 钟山县| 溧水县| 延津县| 昭觉县| 屏东县| 漠河县| 筠连县| 类乌齐县| 安岳县| 古田县| 白朗县|