25

これは、jpa一括挿入に関するいくつかのトピックを読んだ後に作成した簡単な例です.2つの永続オブジェクトUserとSiteがあります. 1 人のユーザーが複数のサイトを持つことができるため、ここでは 1 対多の関係があります。ユーザーを作成し、いくつかのサイトをユーザー アカウントに作成/リンクしたいとします。Site オブジェクトに一括挿入を使用する意思があることを考慮して、コードは次のようになります。

User user = new User("John Doe");

user.getSites().add(new Site("google.com", user));
user.getSites().add(new Site("yahoo.com", user));

EntityTransaction tx = entityManager.getTransaction();
tx.begin();
entityManager.persist(user);
tx.commit();

しかし、このコードを実行すると (jpa 実装プロバイダーとして hibernate を使用しています)、次の sql 出力が表示されます。

Hibernate: insert into User (id, name) values (null, ?)
Hibernate: call identity()
Hibernate: insert into Site (id, url, user_id) values (null, ?, ?)
Hibernate: call identity()
Hibernate: insert into Site (id, url, user_id) values (null, ?, ?)
Hibernate: call identity()

つまり、「実際の」一括挿入が機能しないということですか、それとも混乱していますか?

このサンプル プロジェクトのソース コードは次のとおりです。これは maven プロジェクトなので、ダウンロードして mvn install を実行するだけで、出力を確認できます。

更新しました:

Ken Liu が親切にアドバイスした後、サイト オブジェクト ID の自動生成を無効にしました。

    User user = new User("John Doe");
    user.getSites().add(new Site(1, "google.com", user));
    user.getSites().add(new Site(2, "yahoo.com", user));
    entityManager.setFlushMode(FlushModeType.COMMIT);
    EntityTransaction tx = entityManager.getTransaction();
    tx.begin();
    entityManager.persist(user);
    tx.commit();

今、デバッグ出力に次の行があります。

デバッグ: org.hibernate.jdbc.AbstractBatcher - 実行バッチ サイズ: 2

できます!

4

3 に答える 3

21

データベースを使用して ID を生成している場合、Hibernate はクエリを実行して各エンティティの主キーを生成する必要があります。

于 2010-05-05T13:15:29.273 に答える
7

一括挿入のために休止状態をバイパスする方がはるかに効率的であることがわかりました。ORM (オブジェクト リレーショナル マッピング) を廃止する必要がありますが、現在のセッションに関連付けられた接続とトランザクション管理を引き続き活用できます。

ORM の利便性は一時的に失われますが、特に hibernate は通常SELECT、各INSERT.

Session.doWorkこれを容易にするのに非常に便利です。

private MyParentObject saveMyParentObject(final MyParentObject parent, final List<MyChildObject> children)
{
    transaction = session.beginTransaction();
    try
    {
        session.save(parent); // NOTE: parent.parentId assigned and returned here

        session.doWork(new Work()
        {
            public void execute(Connection con) throws SQLException
            {
                // hand written insert SQL - can't use hibernate
                PreparedStatement st = con.prepareStatement("INSERT INTO my_child (parent_id, name, ...) values (?, ?, ...)");

                for (MyChildObject child : children)
                {
                    MyChildObject child = new MyChildObject();
                    child.setParentId(parent.getParentId()); // assign parent id for foreign key

                    // hibernate can't help, determine jdbc parameters manually
                    st.setLong(1, child.getParentId());
                    st.setString(2, child.getName());
                    ...
                    st.addBatch();
                }

                // NOTE: you may want to limit the size of the batch
                st.executeBatch();
            }
        });

        // if your parent has a OneToMany relationship with child(s), refresh will populate this 
        session.refresh(parent);
        transaction.commit();
        return parent;
    }
    catch(Throwable e)
    {
        transaction.rollback();
        throw new RuntimeException(e);
    }   
}
于 2014-10-28T23:53:41.310 に答える
5

バッチ挿入の落とし穴について説明する短いブログを書きました。また、Hibernate でバッチ挿入を開始するためのすべての適切な構成を持つ小さなプロジェクトへのポインターもあります。http://sensiblerationalization.blogspot.com/2011/03/quick-tip-on-hibernate-batch-operation.htmlで詳細を参照してください。

于 2011-03-09T03:21:09.510 に答える