カテゴリ: Spring 更新日: 2025/11/19

Springでバルク更新・一括挿入を高速に!flush・clearの使い方と最適化のベストプラクティス

バルク更新/一括挿入の注意点:フラッシュ・クリアと高速化ベストプラクティス
バルク更新/一括挿入の注意点:フラッシュ・クリアと高速化ベストプラクティス

先生と生徒の会話形式で理解しよう

生徒

「Spring Bootで大量データを一括登録したいんですけど、普通にsave()を繰り返すだけじゃダメなんですか?」

先生

「確かにそれでも動くけど、パフォーマンスは落ちるし、メモリも無駄に使ってしまいます。flush()clear()を使って、効率的に処理する方法があるんですよ。」

生徒

「flushとclearって聞いたことはあるけど、いまいち意味がわからなくて…」

先生

「それなら、一括挿入やバルク更新の注意点とあわせて、パフォーマンスを上げるベストプラクティスを丁寧に解説していきましょう!」

1. Springにおけるバルク処理とは?

1. Springにおけるバルク処理とは?
1. Springにおけるバルク処理とは?

バルク処理とは、大量のレコードを一括で登録・更新・削除する処理のことです。Spring Data JPAでも、通常のsave()delete()メソッドを使ってバルク処理はできますが、ループ処理のまま実行すると性能が大幅に劣化します。

それは、エンティティがすべてPersistenceContextに保持され、メモリ消費が増えるためです。さらに、flushのタイミングが遅れることで、データベースへの反映が遅延することもあります。

2. flushとclearの基本:高速化の鍵

2. flushとclearの基本:高速化の鍵
2. flushとclearの基本:高速化の鍵

flushは、エンティティの変更を即座にデータベースに反映させる命令です。clearは、エンティティマネージャが管理しているキャッシュ(1次キャッシュ)をクリアしてメモリを解放する命令です。

これらを適切に組み合わせることで、一括挿入やバルク更新時のパフォーマンスを飛躍的に向上させることができます。

3. saveAllだけでは不十分?バルク挿入の落とし穴

3. saveAllだけでは不十分?バルク挿入の落とし穴
3. saveAllだけでは不十分?バルク挿入の落とし穴

Spring Data JPAのsaveAll()は便利ですが、内部ではEntityManager.persist()が1件ずつ呼ばれており、flushもclearも自動で呼ばれないため、大量データには向いていません。

そこで、batchSizeごとにflush()clear()を呼ぶように手動で制御する必要があります。


@Autowired
private EntityManager entityManager;

@Transactional
public void bulkInsert(List<User> users) {
    for (int i = 0; i < users.size(); i++) {
        entityManager.persist(users.get(i));
        if (i % 50 == 0) {
            entityManager.flush();
            entityManager.clear();
        }
    }
}

このように、一定件数ごとに明示的にflush/clearを行うことで、メモリリーク防止・性能向上を両立できます。

4. バルク更新の注意点と副作用

4. バルク更新の注意点と副作用
4. バルク更新の注意点と副作用

JPAでは、JPQLで直接UPDATE文を発行するバルク更新が可能ですが、この場合PersistenceContextの内容と実際のデータベースの状態が一致しなくなります。


@Modifying
@Query("UPDATE User u SET u.status = 'ACTIVE' WHERE u.lastLogin < :threshold")
int activateUsers(@Param("threshold") LocalDateTime threshold);

このようなバルク更新を行ったあとに、同じエンティティを参照しようとすると古い値が返ることがあるため、必要に応じてclear()や再取得が必要です。

5. 高速化のためのJPAプロパティ設定

5. 高速化のためのJPAプロパティ設定
5. 高速化のためのJPAプロパティ設定

JPAのパフォーマンスを最適化するために、Hibernateのbatchサイズを設定するのが効果的です。


spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true

これにより、Hibernateが内部的にバッチ処理を最適化して、SQLの発行回数を大幅に削減してくれます。

6. Repository vs EntityManager:どちらを使うべき?

6. Repository vs EntityManager:どちらを使うべき?
6. Repository vs EntityManager:どちらを使うべき?

Spring DataのCrudRepositoryJpaRepositoryはシンプルで使いやすいですが、バルク処理の最適化にはEntityManagerの細かい制御が有効です。

エンティティが大量にある場面や、flushタイミングを自分で調整したいケースでは、EntityManagerを使ったほうが効率的です。

一方、通常の登録処理や小規模な更新であれば、save()saveAll()の使用で問題ありません。

7. バルク削除の実装と注意点

7. バルク削除の実装と注意点
7. バルク削除の実装と注意点

一括削除についても、deleteAll()はあまり効率が良くなく、JPQLでDELETE文を直接実行した方が速い場合があります。


@Modifying
@Query("DELETE FROM User u WHERE u.status = 'INACTIVE'")
int deleteInactiveUsers();

ただし、この方法もPersistenceContextと同期が取れなくなるため、必要ならflushやclearで整合性を保つのが重要です。

8. バルク処理でトランザクションと例外に注意

8. バルク処理でトランザクションと例外に注意
8. バルク処理でトランザクションと例外に注意

大量データを扱うと、途中で例外が発生したときのロールバックにも気をつける必要があります。

@Transactionalを付けて一括で処理する場合、どこかで例外が発生すると、すべてロールバックされるため、適切な単位でトランザクションを分割するのが安全です。

リトライ処理や、障害発生時のログ記録などもあらかじめ設計に組み込んでおきましょう。

カテゴリの一覧へ
新着記事
Javaの@FunctionalInterfaceアノテーションの使い方を完全ガイド!初心者でもわかる関数型インターフェース
JavaのHashSetのaddメソッドを完全ガイド!初心者でもわかるセットの使い方
SpringのBindingResultを完全ガイド!初心者でもわかる入力チェックとエラー処理
Spring Securityでフォームログインを実装!ログイン・ログアウト・Remember-Meの設定方法まとめ
人気記事
No.1
Java&Spring記事人気No1
JavaのExceptionクラスを完全解説!初心者でも理解できる例外処理の基本
No.2
Java&Spring記事人気No2
Spring BootとJavaの互換性一覧!3.5/3.4/3.3はJava 21・17に対応してる?
No.3
Java&Spring記事人気No3
JavaのDateクラスの使い方を完全ガイド!初心者でもわかる日付操作
No.4
Java&Spring記事人気No4
Javaのラムダ式anyMatchの使い方:containsやList検索の実践テクニック
No.5
Java&Spring記事人気No5
Javaのラムダ式でListを抽出&変換!filterとmapでスマートに操作
No.6
Java&Spring記事人気No6
JSPで使えるJavaコードの書き方を徹底解説!初心者向けルールと制限ポイント
No.7
Java&Spring記事人気No7
Spring BootのJakarta移行ガイド!初心者向けjavax→jakarta変更ポイント徹底解説
No.8
Java&Spring記事人気No8
Javaのcountの使い方を完全ガイド!ラムダ式で件数カウントの定石をマスターしよう