🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Hibernate

HibernateとはJava言語のobject-relational mapping (ORM)ライブラリであり、Object/Relational Mappingよりはるか多くの方法でアプリケーションをPOJOで機能付けることができます。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Spring

Spring Framework は、Javaプラットフォーム向けのオープンソースアプリケーションフレームワークです。 Java Platform上に、 Web ベースのアプリケーションを設計するための拡張機能が数多く用意されています。

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

Q&A

解決済

1回答

18264閲覧

spring-data-jpaでbulk insertするにはentity manager を使うしかないのでしょうか。

motisen

総合スコア92

Hibernate

HibernateとはJava言語のobject-relational mapping (ORM)ライブラリであり、Object/Relational Mappingよりはるか多くの方法でアプリケーションをPOJOで機能付けることができます。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Spring

Spring Framework は、Javaプラットフォーム向けのオープンソースアプリケーションフレームワークです。 Java Platform上に、 Web ベースのアプリケーションを設計するための拡張機能が数多く用意されています。

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

1グッド

3クリップ

投稿2019/10/24 13:28

編集2019/10/25 05:36

お世話になります。

spring-boot 1.4
spring-data-jpa 1.10
を使用しています。

バッチ処理の速度改善に取り組んでいますが、100件ほど入れるinsertがbulk insertではないことに気づきました。
(この100件ほどのinsertはバッチ内で何度も実行されます。)
bulk insertに変更したいと思い、以下のページを読みましたが、
https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-data-access
https://stackoverflow.com/questions/34228044/how-to-enable-batch-inserts-with-hibernate-and-spring-boot
https://www.baeldung.com/spring-data-jpa-batch-inserts
どれもentity managerを使うような仕組みでした。

そのように実装してもよいのですが、どちらかといえば、
saveメソッド(JpaRepository)

Java

1 <S extends T> List<S> save(Iterable<S> entities);

の動きをbulk insertに変更できないかと考えています。

お知恵を貸していただきたいです。
よろしくお願いします。

---情報を追加しました。---
DB : PostgreSQL 9.5
エンティティクラスの主キー部分のソースコード

Java

1 @Id 2 @SequenceGenerator( name = "hoge", 3 sequenceName = "piyo", 4 allocationSize = 1 ) 5 @GeneratedValue(strategy = GenerationType.SEQUENCE, 6 generator = "hoge" ) 7 @Column 8 private Long fooBarId; 9
A-pZ👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rubytomato

2019/10/25 04:28

利用されているデータベースの種類とバージョン、バルクインサートしたいエンティティクラスの主キーの部分のソースコード(@Idを付けているフィールドのこと)を質問内容に追記してください。
motisen

2019/10/25 05:37

情報追記致しました。ご確認よろしくお願い致します。
guest

回答1

0

ベストアンサー

質問内容の追記ありがとうございました。

下記の環境で、リポジトリのメソッドを使ってコレクションの永続化にバルクインサートが行われるか確認しました。
この調査で参考にしたサイトは『Batch Insert/Update with Hibernate/JPA』です。

また、ご質問内容のSpring Bootのバージョンと、今回検証に使用したSpring Bootのバージョンが大きく異なりますので、この回答があてにならない可能性がありますが、そのときはご了承ください。

環境

  • Windows 10 Professional 1903
  • OpenJDK 11.0.2
  • Spring Boot 2.2.0
  • Spring Data JPA 2.2.0
  • Hibernate Core 5.4.6
  • postgresql 42.2.8 (JDBC Driver)

データベース

データベースはPostgreSQL 9.6.1です。

memoテーブル
CREATE TABLE memo ( id bigserial NOT NULL, title varchar(255) NOT NULL, description text NOT NULL, done boolean DEFAULT false NOT NULL, updated timestamp without time zone DEFAULT current_timestamp NOT NULL, CONSTRAINT memo_pkey PRIMARY KEY(id) );

memoテーブルの主キー用シーケンスの増分数を100へ変更

ALTER SEQUENCE memo_id_seq INCREMENT BY 100;

Spring Boot

アプリケーション設定ファイル(application.yml)
spring: datasource: url: jdbc:postgresql://localhost:5432/test_db?reWriteBatchedInserts=true username: test_user password: test_user hikari: connection-test-query: select 1 connection-timeout: 10000 maximum-pool-size: 2 minimum-idle: 2 jpa: open-in-view: true show-sql: true properties: hibernate: generate_statistics: true order_inserts: true format_sql: true dialect: org.hibernate.dialect.PostgreSQL95Dialect jdbc: batch_size: 100
エンティティクラス
@Entity @Table(name = "memo") public class Memo implements Serializable { private static final long serialVersionUID = 7104170344304358209L; @Id @SequenceGenerator(name = "memo_id_seq", sequenceName = "memo_id_seq", allocationSize = 100) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "memo_id_seq") private Long id; @Column(name = "title", nullable = false) private String title; @Column(name = "description", nullable = false) private String description; @Column(name = "done", nullable = false) private Boolean done; @Column(name = "updated", nullable = false) private LocalDateTime updated; // アクセサは省略 }
リポジトリ
public interface MemoRepository extends JpaRepository<Memo, Long> { }
サービスクラス

MemoクラスのコレクションをリポジトリのsaveAllメソッドで永続化する部分の抜粋です。
Spring Data JPA 2.xからコレクションを永続化するメソッドはsaveAllに変わっています。(Spring Data JPA 1.xではsave

Interface JpaRepository<T,ID> - 1.5.0.RELEASE
Interface JpaRepository<T,ID> - 2.2.0.RELEASE

@Transactional(readOnly = false) @Override public void save(List<Memo> entities) { log.info("save all start"); memoRepository.saveAll(entities); log.info("save all end"); }

また、比較用にエンティティマネージャを使った永続化のコードです。

@Transactional(readOnly = false) @Override public void save2(List<Memo> entities) { log.info("save2 all start"); for (int i=0; i<entities.size(); i++) { entityManager.persist(entities.get(i)); if (i> 0 && i % 100 == 0) { entityManager.flush(); entityManager.clear(); } } entityManager.flush(); entityManager.clear(); log.info("save2 all end"); }

動作確認

バルクインサートについて

リポジトリのsaveAllメソッドでもバルクインサートが行われるようにするには、下記2点の設定が必要でした。

  • 1つ目は、バッチサイズ(spring.jpa.properties.hibernate.jdbc.batch_size)を設定すること。
  • 2つ目は、JDBC URLにreWriteBatchedInserts=trueを付けること。

2つ目についてはJDBCドライバレベルで影響が出る設定なので、こちら『Connecting to the Database』などでご確認ください。

比較用にエンティティマネージャを使った場合の動作確認も行いましたが、エンティティマネージャを使う方は、100件単位でflush/clearを行っています。(無駄なキャッシュをクリアするため)

リポジトリの方では行っていないのですが、メモリの使用量が気になる場合は同じような処理を入れた方がいいかもしれません。

バルクインサートが行われたかどうかの確認

PostgreSQLのログからバルクインサートが行われていることを確認しました。

LOG: 実行 <unnamed>: insert into memo (description, done, title, updated, id) values ($1, $2, $3, $4, $5),($6, $7, $8, $9, $10),($11, $12, $13, $14, $15),($16, $17, $18, $19, $20),($21, $22, $23, $24, $25),($26, $27, $28, $29, $30),($31, $32, $33, $34, $35),($36, $37, $38, $39, $40),($41, $42, $43, $44, $45),($46, $47, $48, $49, $50),($51, $52, $53, $54, $55),($56, $57, $58, $59, $60),($61, $62, $63, $64, $65),($66, $67, $68, $69, $70),($71, $72, $73, $74, $75),($76, $77, $78, $79, $80),($81, $82, $83, $84, $85),($86, $87, $88, $89, $90),($91, $92, $93, $94, $95),($96, $97, $98, $99, $100),($101, $102, $103, $104, $105),($106, $107, $108, $109, $110),($111, $112, $113, $114, $115),($116, $117, $118, $119, $120),($121, $122, $123, $124, $125),($126, $127, $128, $129, $130),($131, $132, $133, $134, $135),($136, $137, $138, $139, $140),($141, $142, $143, $144, $145),($146, $147, $148, $149, $150),($151, $152, $153, $154, $155),($156, $157, $158, $159, $160),($161, $162, $163, $164, $165),($166, $167, $168, $169, $170),($171, $172, $173, $174, $175),($176, $177, $178, $179, $180),($181, $182, $183, $184, $185),($186, $187, $188, $189, $190),($191, $192, $193, $194, $195),($196, $197, $198, $199, $200),($201, $202, $203, $204, $205),($206, $207, $208, $209, $210),($211, $212, $213, $214, $215),($216, $217, $218, $219, $220),($221, $222, $223, $224, $225),($226, $227, $228, $229, $230),($231, $232, $233, $234, $235),($236, $237, $238, $239, $240),($241, $242, $243, $244, $245),($246, $247, $248, $249, $250),($251, $252, $253, $254, $255),($256, $257, $258, $259, $260),($261, $262, $263, $264, $265),($266, $267, $268, $269, $270),($271, $272, $273, $274, $275),($276, $277, $278, $279, $280),($281, $282, $283, $284, $285),($286, $287, $288, $289, $290),($291, $292, $293, $294, $295),($296, $297, $298, $299, $300),($301, $302, $303, $304, $305),($306, $307, $308, $309, $310),($311, $312, $313, $314, $315),($316, $317, $318, $319, $320)

性能

以下はバッチサイズを100と設定した状態と設定しなかった状態で、Memoクラスのコレクションが1万,5万,10万件を永続化したときにかかった時間です。

件数リポジトリ (batch_size=100)リポジトリ (batch_size未指定)エンティティマネージャ (batch_size=100)エンティティマネージャ (batch_size未指定)
1万件1.075秒3.044秒0.925秒2.918秒
5万件3.74秒14.081秒3.269秒13.483秒
10万件6.862秒27.644秒6.282秒26.519秒

投稿2019/10/25 14:14

rubytomato

総合スコア1752

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

motisen

2019/10/28 05:29

とても詳しいご回答ありがとうございます! 私の環境でも reWriteBatchedInserts=true spring.jpa.properties.hibernate.jdbc.batch_size を設定することで、bulk insertとして動作することが確認できました。 性能まで載せて頂き、とても助かりました。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問