web-dev-qa-db-ja.com

Hibernateのさまざまな保存方法の違いは何ですか?

Hibernateには、オブジェクトを取得してデータベースに格納するメソッドがいくつかあります。それらの違いは何ですか、どれを使用するか、何を使用するかを知っているインテリジェントな方法が1つだけではないのはなぜですか?

これまでに特定した方法は次のとおりです。

  • save()
  • update()
  • saveOrUpdate()
  • saveOrUpdateCopy()
  • merge()
  • persist()
196
Henrik Paul

これがメソッドの理解です。主にこれらは API に基づいていますが、実際にはこれらのすべてを使用しているわけではありません。

saveOrUpdateいくつかのチェックに応じて、保存または更新を呼び出します。例えば。識別子が存在しない場合、saveが呼び出されます。それ以外の場合、更新が呼び出されます。

saveエンティティを永続化します。存在しない場合は識別子を割り当てます。もしそうなら、それは本質的に更新を行っています。生成されたエンティティのIDを返します。

update既存の識別子を使用してエンティティを永続化しようとします。識別子が存在しない場合、例外がスローされると思います。

saveOrUpdateCopyこれは非推奨であり、使用しないでください。代わりに...

merge今、ここから私の知識が揺らぎ始めます。ここで重要なのは、一時的なエンティティ、分離されたエンティティ、永続的なエンティティの違いです。オブジェクトの状態の詳細については、 こちらをご覧ください 。保存と更新では、永続オブジェクトを扱います。これらはセッションにリンクされているため、Hibernateは何が変更されたかを認識します。しかし、一時オブジェクトがある場合、セッションは関係しません。これらの場合、更新にはマージを使用し、保存には永続化する必要があります。

persist前述のように、これは一時オブジェクトで使用されます。生成されたIDは返されません。

117
Lee Theobald
╔══════════════╦═══════════════════════════════╦════════════════════════════════╗
║    METHOD    ║            TRANSIENT          ║            DETACHED            ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║              ║       sets id if doesn't      ║   sets new id even if object   ║
║    save()    ║     exist, persists to db,    ║    already has it, persists    ║
║              ║    returns attached object    ║ to DB, returns attached object ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║              ║       sets id on object       ║             throws             ║
║   persist()  ║     persists object to DB     ║       PersistenceException     ║
║              ║                               ║                                ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║              ║                               ║                                ║
║   update()   ║           Exception           ║     persists and reattaches    ║
║              ║                               ║                                ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║              ║  copy the state of object in  ║    copy the state of obj in    ║
║    merge()   ║     DB, doesn't attach it,    ║      DB, doesn't attach it,    ║
║              ║    returns attached object    ║     returns attached object    ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║              ║                               ║                                ║
║saveOrUpdate()║           as save()           ║            as update()         ║
║              ║                               ║                                ║
╚══════════════╩═══════════════════════════════╩════════════════════════════════╝
114
Sergii Shevchyk
  • 永続化と保存の微妙な違いについては、 Hibernate Forum をご覧ください。違いは、INSERTステートメントが最終的に実行される時間であるように見えます。 saveは識別子を返すため、トランザクションの状態に関係なくINSERTステートメントを即座に実行する必要があります(一般に悪いことです)。 Persistは、識別子を割り当てるためだけに、現在実行中のトランザクション以外のステートメントを実行しません。 Save/Persistはtransientインスタンスで動作します。つまり、まだ識別子が割り当てられていないインスタンスはDBに保存されません。

  • UpdateおよびMerge両方がで動作しますインスタンス、つまり、DBに対応するエントリがあるが、現在セッションにアタッチされていない(またはセッションによって管理されていない)インスタンス。それらの違いは、関数に渡されるインスタンスに何が起こるかです。 updateは、インスタンスを再接続しようとします。つまり、現在セッションに接続されている永続エンティティの他のインスタンスが存在しない可能性があり、そうでない場合は例外がスローされます。 mergeただし、セッション内の永続インスタンスにすべての値をコピーします(現在ロードされていない場合はロードされます)。入力オブジェクトは変更されません。 mergeupdateより一般的ですが、より多くのリソースを使用する場合があります。

65
jrudolph

このリンクは良い方法で説明しています:

http://www.stevideter.com/2008/12/07/saveorupdate-versus-merge-in-hibernate/

私たちは皆、これらの問題にまれにしか遭遇しないので、それらをもう一度見ると、これを解決したことがわかりますが、どのように覚えているかはわかりません。

HibernateでSession.saveOrUpdate()を使用するときにスローされるNonUniqueObjectExceptionは私のものの1つです。複雑なアプリケーションに新しい機能を追加します。私のユニットテストはすべてうまくいきます。次に、UIをテストしてオブジェクトを保存しようとすると、「同じ識別子の値を持つ別のオブジェクトが既にセッションに関連付けられています」というメッセージで例外が発生し始めます。Java Persistence with Hibernate 。

            Session session = sessionFactory1.openSession();
            Transaction tx = session.beginTransaction();
            Item item = (Item) session.get(Item.class, new Long(1234));
            tx.commit();
            session.close(); // end of first session, item is detached

            item.getId(); // The database identity is "1234"
            item.setDescription("my new description");
            Session session2 = sessionFactory.openSession();
            Transaction tx2 = session2.beginTransaction();
            Item item2 = (Item) session2.get(Item.class, new Long(1234));
            session2.update(item); // Throws NonUniqueObjectException
            tx2.commit();
            session2.close();

この例外の原因を理解するには、デタッチされたオブジェクトと、デタッチされたオブジェクトでsaveOrUpdate()(または単にupdate())を呼び出したときに何が起こるかを理解することが重要です。

個々のHibernate Sessionを閉じると、作業している永続オブジェクトは切り離されます。つまり、データはアプリケーションのメモリに残っていますが、Hibernateはオブジェクトの変更を追跡する責任を負いません。

その後、切り離されたオブジェクトを変更し、それを更新する場合は、オブジェクトを再接続する必要があります。その再接続プロセス中に、Hibernateは同じオブジェクトの他のコピーがあるかどうかを確認します。見つかった場合は、「実際の」コピーが何であるかがわからないことを通知する必要があります。おそらく、保存する予定の他のコピーに他の変更が加えられた可能性がありますが、Hibernateはそれらを管理していなかったため、それらについては知りません。

Hibernateは、不正なデータを保存するのではなく、NonUniqueObjectExceptionを介して問題について通知します。

それで、私たちは何をしますか? Hibernate 3にはmerge()があります(Hibernate 2にはsaveOrUpdateCopy()を使用します)。このメソッドは、Hibernateが他のデタッチされたインスタンスからの変更を保存したいインスタンスに強制的にコピーするため、保存前にメモリ内のすべての変更をマージします。

        Session session = sessionFactory1.openSession();
        Transaction tx = session.beginTransaction();
        Item item = (Item) session.get(Item.class, new Long(1234));
        tx.commit();
        session.close(); // end of first session, item is detached

        item.getId(); // The database identity is "1234"
        item.setDescription("my new description");
        Session session2 = sessionFactory.openSession();
        Transaction tx2 = session2.beginTransaction();
        Item item2 = (Item) session2.get(Item.class, new Long(1234));
        Item item3 = session2.merge(item); // Success!
        tx2.commit();
        session2.close();

マージでは、インスタンスの新しく更新されたバージョンへの参照が返されることに注意することが重要です。セッションにアイテムを再添付するものではありません。インスタンスの等価性(item == item3)をテストすると、この場合falseが返されることがわかります。この時点から、おそらくitem3を使用したいと思うでしょう。

また、Java Persistence API(JPA)には、オブジェクトの分離および再接続の概念がなく、EntityManager.persist()およびEntityManager.merge()を使用することに注意することも重要です。

一般に、Hibernateを使用する場合、saveOrUpdate()は通常、私のニーズに十分であることがわかりました。通常、同じタイプのオブジェクトへの参照を持つオブジェクトがある場合にのみ、マージを使用する必要があります。ごく最近、例外の原因は、参照が再帰的ではないことを検証するコードにありました。検証の一部として同じオブジェクトをセッションにロードしていたため、エラーが発生しました。

この問題はどこで発生しましたか?マージはうまくいきましたか、または別のソリューションが必要でしたか?常にマージを使用しますか、それとも特定の場合に必要な場合にのみ使用しますか?

12
HakunaMatata

すべてのhibernate saveメソッドの違いを示す良い例を見つけました:

http://www.journaldev.com/3481/hibernate-session-merge-vs-update-save-saveorupdate-persist-example

簡単に言えば、上記のリンクによると:

save()

  • トランザクションの外部でこのメソッドを呼び出すことができます。トランザクションなしでこれを使用し、エンティティ間でカスケードしている場合、セッションをフラッシュしない限り、プライマリエンティティのみが保存されます。
  • そのため、プライマリオブジェクトからマップされた他のオブジェクトがある場合、それらはトランザクションのコミット時またはセッションのフラッシュ時に保存されます。

persist()

  • トランザクションでsave()を使用することに似ているため、安全で、カスケードされたオブジェクトを処理します。

saveOrUpdate()

  • トランザクションの有無にかかわらず使用できます。save()と同様に、トランザクションなしで使用した場合、マップされたエンティティは保存されません。セッションをフラッシュします。

  • 提供されたデータに基づいて、クエリを挿入または更新します。データベースにデータが存在する場合、更新クエリが実行されます。

update()

  • Hibernate updateは、エンティティ情報のみを更新していることがわかっている場合に使用する必要があります。この操作により、エンティティオブジェクトが永続的なコンテキストに追加され、トランザクションがコミットされると、さらに変更が追跡および保存されます。
  • したがって、updateを呼び出した後でも、エンティティに値を設定すると、トランザクションがコミットされたときに値が更新されます。

merge()

  • Hibernate mergeは既存の値を更新するために使用できますが、このメソッドは渡されたエンティティオブジェクトからコピーを作成し、それを返します。返されたオブジェクトは永続コンテキストの一部であり、変更が追跡され、渡されたオブジェクトは追跡されません。これは、他のすべてのメソッドとのmerge()の大きな違いです。

また、これらすべての実用的な例については、上記のリンクを参照してください。これらのすべての異なる方法の例を示しています。

5
OutOfMind

実際、hibernate save()メソッドとpersist()メソッドの違いは、使用しているジェネレータークラスによって異なります。

ジェネレータークラスが割り当てられている場合、save()メソッドとpersist()メソッドの間に違いはありません。ジェネレーターは「割り当てられた」ことを意味するため、プログラマーとしてデータベースに保存する主キー値を指定する必要があります[このジェネレーターの概念を知っていることを願っています] hibernate it selfは、プライマリキーid値をデータベースの右に割り当てます[割り当てられたジェネレーター以外、hibernateはプライマリキーid値を保持するためにのみ使用されるため、この場合、save()またはpersist()メソッドは、データベースにレコードを通常挿入しますが、save()メソッドは、hibernateによって生成された主キーID値を返すことができ、

long s = session.save(k);

これと同じ場合、persist()はクライアントに値を返しません。

5
Hari Krishna

デタッチされたオブジェクトで更新を呼び出すと、オブジェクトを変更したかどうかに関係なく、データベースで常に更新が行われることに注意してください。希望どおりでない場合は、LockMode.NoneでSession.lock()を使用する必要があります。

Updateは、オブジェクトが現在のセッションの範囲外で変更された場合にのみ呼び出す必要があります(デタッチモードの場合)。

2
bernardn

この記事 で説明したように、ほとんどの場合はJPAメソッドを、バッチ処理タスクにはupdateを好むはずです。

JPAまたはHibernateエンティティは、次の4つの状態のいずれかになります。

  • トランジェント(新規)
  • マネージド(永続的)
  • 戸建
  • 削除済み(削除済み)

ある状態から別の状態への遷移は、EntityManagerまたはSessionメソッドを介して行われます。

たとえば、JPA EntityManagerは、次のエンティティ状態遷移メソッドを提供します。

enter image description here

Hibernate SessionはすべてのJPA EntityManagerメソッドを実装し、savesaveOrUpdateupdateなどの追加のエンティティ状態遷移メソッドを提供します。

enter image description here

持続する

エンティティの状態をTransient(New)からManaged(Persisted)に変更するには、JPA persistによって提供されるEntityManagerメソッドを使用できます。これは、Hibernate Sessionによっても継承されます。

persistメソッドは、PersistEvent Hibernateイベントリスナーによって処理されるDefaultPersistEventListenerをトリガーします。

したがって、次のテストケースを実行する場合:

doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    LOGGER.info(
        "Persisting the Book entity with the id: {}", 
        book.getId()
    );
});

Hibernateは次のSQLステートメントを生成します。

CALL NEXT VALUE FOR hibernate_sequence

-- Persisting the Book entity with the id: 1

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

idエンティティは、Bookエンティティを現在の永続コンテキストにアタッチする前に割り当てられることに注意してください。これは、管理対象エンティティがMap構造体に格納され、キーがエンティティタイプとその識別子で形成され、値がエンティティ参照であるために必要です。これが、JPA EntityManagerおよびHibernate SessionがFirst-Level Cacheとして知られている理由です。

persistを呼び出すと、エンティティは現在実行中の永続コンテキストにのみ接続され、flushが呼び出されるまでINSERTを延期できます。

唯一の例外は IDENTITYジェネレーター です。これは、エンティティ識別子を取得できる唯一の方法であるため、すぐにINSERTをトリガーします。このため、HibernateはIDENTITYジェネレーターを使用してエンティティの挿入をバッチ処理できません。このトピックの詳細については、 この記事 をご覧ください。

セーブ

Hibernate固有のsaveメソッドはJPAより前のバージョンであり、Hibernateプロジェクトの開始以来利用可能になっています。

saveメソッドは、SaveOrUpdateEvent Hibernateイベントリスナーによって処理されるDefaultSaveOrUpdateEventListenerをトリガーします。したがって、saveメソッドは、updateおよびsaveOrUpdateメソッドと同等です。

saveメソッドの動作を確認するには、次のテストケースを検討してください。

doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    Session session = entityManager.unwrap(Session.class);

    Long id = (Long) session.save(book);

    LOGGER.info(
        "Saving the Book entity with the id: {}", 
        id
    );
});

上記のテストケースを実行すると、Hibernateは次のSQLステートメントを生成します。

CALL NEXT VALUE FOR hibernate_sequence

-- Saving the Book entity with the id: 1

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

ご覧のとおり、結果はpersistメソッド呼び出しと同じです。ただし、persistとは異なり、saveメソッドはエンティティ識別子を返します。

詳細については、 この記事 をご覧ください。

更新

Hibernate固有のupdateメソッドは、 ダーティチェックメカニズム をバイパスし、フラッシュ時にエンティティの更新を強制することを目的としています。

updateメソッドは、SaveOrUpdateEvent Hibernateイベントリスナーによって処理されるDefaultSaveOrUpdateEventListenerをトリガーします。したがって、updateメソッドは、saveおよびsaveOrUpdateメソッドと同等です。

updateメソッドがどのように機能するかを確認するには、1つのトランザクションでBookエンティティを保持し、エンティティが切り離された状態でそれを変更し、updateメソッド呼び出しを使用してSQL UPDATEを強制する次の例を考えます。

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

LOGGER.info("Modifying the Book entity");

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);

    session.update(_book);

    LOGGER.info("Updating the Book entity");
});

上記のテストケースを実行すると、Hibernateは次のSQLステートメントを生成します。

CALL NEXT VALUE FOR hibernate_sequence

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

-- Modifying the Book entity
-- Updating the Book entity

UPDATE 
    book 
SET 
    author = 'Vlad Mihalcea', 
    isbn = '978-9730228236', 
    title = 'High-Performance Java Persistence, 2nd edition'
WHERE 
    id = 1

UPDATEは、永続コンテキストのフラッシュ中、コミットの直前に実行されることに注意してください。そのため、Updating the Book entityメッセージが最初に記録されます。

@SelectBeforeUpdateを使用して不要な更新を回避する

これで、エンティティが切り離された状態で変更されなかった場合でも、UPDATEは常に実行されます。これを防ぐには、@SelectBeforeUpdate Hibernateアノテーションを使用して、loaded stateをフェッチしたSELECTステートメントをトリガーし、ダーティチェックメカニズムで使用します。

そのため、Bookエンティティに@SelectBeforeUpdateアノテーションを付けた場合:

@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {

    //Code omitted for brevity
}

そして、次のテストケースを実行します。

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);

    session.update(_book);
});

Hibernateは次のSQLステートメントを実行します。

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

SELECT 
    b.id,
    b.author AS author2_0_,
    b.isbn AS isbn3_0_,
    b.title AS title4_0_
FROM 
    book b
WHERE 
    b.id = 1

今回は、Hibernateのダーティチェックメカニズムがエンティティが変更されていないことを検出したため、UPDATEが実行されないことに注意してください。

SaveOrUpdate

Hibernate固有のsaveOrUpdateメソッドは、単にsaveおよびupdateのエイリアスです。

saveOrUpdateメソッドは、SaveOrUpdateEvent Hibernateイベントリスナーによって処理されるDefaultSaveOrUpdateEventListenerをトリガーします。したがって、updateメソッドは、saveおよびsaveOrUpdateメソッドと同等です。

次の例に示すように、エンティティを永続化する場合、またはsaveOrUpdateを強制する場合は、UPDATEを使用できます。

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    Session session = entityManager.unwrap(Session.class);
    session.saveOrUpdate(book);

    return book;
});

_book.setTitle("High-Performance Java Persistence, 2nd edition");

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);
    session.saveOrUpdate(_book);
});

NonUniqueObjectExceptionに注意してください

saveupdate、およびsaveOrUpdateで発生する可能性のある問題の1つは、永続コンテキストに次の例と同じIDと同じタイプのエンティティ参照が既に含まれている場合です。

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    Session session = entityManager.unwrap(Session.class);
    session.saveOrUpdate(book);

    return book;
});

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

try {
    doInJPA(entityManager -> {
        Book book = entityManager.find(
            Book.class, 
            _book.getId()
        );

        Session session = entityManager.unwrap(Session.class);
        session.saveOrUpdate(_book);
    });
} catch (NonUniqueObjectException e) {
    LOGGER.error(
        "The Persistence Context cannot hold " +
        "two representations of the same entity", 
        e
    );
}

ここで、上記のテストケースを実行すると、HibernateはNonUniqueObjectExceptionをスローします。これは、2番目のEntityManagerBookに渡す識別子と同じ識別子を持つupdateエンティティが既に含まれており、永続コンテキストが同じエンティティの2つの表現を保持できないためです。

org.hibernate.NonUniqueObjectException: 
    A different object with the same identifier value was already associated with the session : [com.vladmihalcea.book.hpjp.hibernate.pc.Book#1]
    at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.Java:651)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.Java:284)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.Java:227)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.Java:92)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.Java:73)
    at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.Java:682)
    at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.Java:674)

マージ

NonUniqueObjectExceptionを回避するには、JPA mergeによって提供され、Hibernate EntityManagerによって継承されるSessionメソッドを使用する必要があります。

この記事 で説明したように、永続コンテキストでエンティティ参照が見つからない場合、mergeはデータベースから新しいエンティティスナップショットを取得し、mergeメソッドに渡された分離されたエンティティの状態をコピーします。

mergeメソッドは、MergeEvent Hibernateイベントリスナーによって処理されるDefaultMergeEventListenerをトリガーします。

mergeメソッドがどのように機能するかを確認するには、1つのトランザクションでBookエンティティを永続化し、エンティティが切り離された状態にある間にそれを変更し、サブシーケンス永続コンテキストで切り離されたエンティティをmergeに渡す次の例を考えてください。

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

LOGGER.info("Modifying the Book entity");

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

doInJPA(entityManager -> {
    Book book = entityManager.merge(_book);

    LOGGER.info("Merging the Book entity");

    assertFalse(book == _book);
});

上記のテストケースを実行すると、Hibernateは次のSQLステートメントを実行しました。

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

-- Modifying the Book entity

SELECT 
    b.id,
    b.author AS author2_0_,
    b.isbn AS isbn3_0_,
    b.title AS title4_0_
FROM 
    book b
WHERE 
    b.id = 1

-- Merging the Book entity

UPDATE 
    book 
SET 
    author = 'Vlad Mihalcea', 
    isbn = '978-9730228236', 
    title = 'High-Performance Java Persistence, 2nd edition'
WHERE 
    id = 1

mergeによって返されるエンティティ参照は、mergeメソッドに渡したデタッチされたものとは異なることに注意してください。

現在、デタッチされたエンティティの状態をコピーするときにJPA mergeを使用することをお勧めしますが、バッチ処理タスクを実行するときに余分なSELECTが問題になる可能性があります。

このため、現在実行中の永続コンテキストに既にエンティティ参照がアタッチされておらず、分離されたエンティティが変更されていることが確実な場合は、updateを使用することをお勧めします。

このトピックの詳細については、 この記事 をご覧ください。

結論

エンティティを永続化するには、JPA persistメソッドを使用する必要があります。切り離されたエンティティの状態をコピーするには、mergeを優先する必要があります。 updateメソッドは、バッチ処理タスクにのみ役立ちます。 savesaveOrUpdateupdateの単なるエイリアスであり、おそらくまったく使用しないでください。

一部の開発者は、エンティティが既に管理されている場合でもsaveを呼び出しますが、管理エンティティの場合、UPDATEは永続コンテキストフラッシュ時に自動的に処理されるため、これは間違いであり、冗長イベントをトリガーします。

詳細については、 この記事 をご覧ください。

2
Vlad Mihalcea

次の答えはどれも正しくありません。これらの方法はすべて同じように見えますが、実際にはまったく異なることを行います。短いコメントをするのは難しいです。これらのメソッドに関する完全なドキュメントへのリンクを提供する方が良い: http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/objectstate.html

1
Anton Popovich

上記の答えはいずれも完全ではありません。レオ・テオバルドの答えは最も近い答えに見えますが。

基本的なポイントは、Hibernateがエンティティの状態をどのように処理し、状態が変化したときにエンティティをどのように処理するかです。フラッシュとコミットに関してもすべてを確認する必要があり、誰もが完全に無視しているようです。

保存方法の休止状態を使用しないでください。休止状態でも存在することを忘れてください!

永続化

誰もが説明したように、Persistは基本的にエンティティを「Transient」状態から「Managed」状態に移行します。この時点で、スラッシュまたはコミットによりinsertステートメントを作成できます。ただし、エンティティは「管理」状態のままです。フラッシュしてもそれは変わりません。

この時点で、再度「永続化」しても変更はありません。そして、永続化されたエンティティを永続化しようとすると、それ以上の保存はありません。

楽しみは、エンティティを追い出そうとしたときに始まります。

エビクトはHibernateの特別な機能であり、エンティティを「管理対象」から「分離」に移行します。分離されたエンティティで永続化を呼び出すことはできません。それを行うと、Hibernateは例外を発生させ、トランザクション全体がコミット時にロールバックされます。

マージと更新

これらは、異なる方法で処理されたときに異なることを行う2つの興味深い関数です。どちらも、エンティティを「切り離された」状態から「管理された」状態に移行しようとしています。しかし、異なる方法でそれを行います。

Detachedは一種の「オフライン」状態を意味するという事実を理解してください。管理対象は「オンライン」状態を意味します。

以下のコードを確認してください。

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();

    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    ses1.merge(entity);

    ses1.delete(entity);

    tx1.commit();

これをするとき?何が起こると思いますか?これが例外を発生させると言った場合、あなたは正しいです。これは、マージがエンティティオブジェクトに対して機能しているため、例外が発生します。これは、切り離された状態です。ただし、オブジェクトの状態は変更されません。

舞台裏では、マージは選択クエリを発生させ、基本的には接続状態のエンティティのコピーを返します。以下のコードを確認してください。

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();
    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    HibEntity copied = (HibEntity)ses1.merge(entity);
    ses1.delete(copied);

    tx1.commit();

上記のサンプルは、マージによって新しいエンティティが永続化された状態のコンテキストに取り込まれたため機能します。

更新を適用しても、実際にはマージのようなエンティティのコピーは取得されないため、同じように機能します。

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();

    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    ses1.update(entity);

    ses1.delete(entity);

    tx1.commit();

デバッグトレースで同時に、UpdateがマージのようなselectのSQLクエリを発生させていないことがわかります。

削除

上記の例では、削除について説明せずに削除を使用しました。削除は基本的に、エンティティを管理状態から「削除」状態に移行します。また、フラッシュまたはコミットされると、保存する削除コマンドが発行されます。

ただし、永続メソッドを使用して、エンティティを「削除」状態から「管理」状態に戻すことができます。

上記の説明が疑問を明確にしたことを願っています。

0
Bharat Raj