web-dev-qa-db-ja.com

多くのエンティティを保持する方法(JPA)

CSVファイルを処理し、各レコード(行)ごとにエンティティを保持する必要があります。今、私はこのようにしています:

_while ((line = reader.readNext()) != null) {
    Entity entity = createEntityObject(line);
    entityManager.save(entity);
    i++;
}
_

ここで、save(Entity)メソッドは基本的に単なるEntityManager.merge()呼び出しです。 CSVファイルには約20,000のエンティティ(行)があります。これは効果的な方法ですか?かなり遅いようです。 EntityManager.persist()を使用する方が良いでしょうか?このソリューションには何らかの欠陥がありますか?

[〜#〜]編集[〜#〜]

これは長いプロセス(400秒以上)であり、persistmergeを使用して両方のソリューションを試しました。どちらも、完了するまでにほぼ同じ時間がかかります(459秒と443秒)。問題は、このようにエンティティを1つずつ保存することが最適かどうかです。私の知る限り、Hibernate(これは私のJPAプロバイダーです)はキャッシュ/フラッシュ機能を実装しているので、これについて心配する必要はありません。

16
John Manak

JPA APIは、これを最適化するためのすべてのオプションを提供しているわけではありません。これをどれだけ速く実行したいかに応じて、ORM固有のオプション(ケースではHibernate)を探す必要があります。

確認すること:

  1. 単一のトランザクションを使用していることを確認します(はい、明らかにこれを確信しています)
  2. JPAプロバイダー(Hibernate)がJDBCバッチAPIを使用していることを確認します(参照:hibernate.jdbc.batch_size)
  3. 生成されたキーの取得をバイパスできるかどうかを確認します(db/jdbcドライバーによって異なりますが、これからどの程度のメリットが得られるかを参照してください。参照:hibernate.jdbc.use_getGeneratedKeys)
  4. カスケードロジックをバイパスできるかどうかを確認します(これによる最小限のパフォーマンス向上のみ)。

したがって、Ebean ORMでは次のようになります。

    EbeanServer server = Ebean.getServer(null);

    Transaction transaction = server.beginTransaction();
    try {
        // Use JDBC batch API with a batch size of 100
        transaction.setBatchSize(100);
        // Don't bother getting generated keys
        transaction.setBatchGetGeneratedKeys(false);
        // Skip cascading persist 
        transaction.setPersistCascade(false);

        // persist your beans ...
        Iterator<YourEntity> it = null; // obviously should not be null 
        while (it.hasNext()) {
            YourEntity yourEntity = it.next();
            server.save(yourEntity);
        }

        transaction.commit();
    } finally {
        transaction.end();
    }

そして、JDBCを介してこれを行う場合は、ORMオーバーヘッド(オブジェクトの作成やガベージコレクションなどが少ない)をスキップします。そのため、このオプションは無視しません。

はい、これはあなたの質問には答えませんが、より多くのORM固有のバッチ挿入の微調整の検索に役立つ可能性があります。

12
Rob Bygrave

これを行う一般的な方法の1つは、トランザクションを使用することです。新しいトランザクションを開始してから多数のオブジェクトを永続化する場合、トランザクションをコミットするまで、それらのオブジェクトは実際にはDBに挿入されません。コミットするアイテムの数が多い場合、これはいくつかの効率を上げることができます。

チェックアウト EntityManager.getTransaction

5
dough

少なくともHibernateではそれをより高速にするために、特定の数の挿入の後にflush()およびclear()を実行します。私は何百万ものレコードに対してこのアプローチを実行しましたが、うまくいきます。まだ遅いですが、やらないよりずっと速いです。基本的な構造は次のとおりです。

int i = 0;
for(MyThingy thingy : lotsOfThingies) {

    dao.save(thingy.toModel())

    if(++i % 20 == 0) {
        dao.flushAndClear();
    }

}
3
egervari

古典的なSQL挿入ステートメントを使用して、データベースに直接書き込むことができます。

@see EntityManager.createNativeQuery

3
Ralph