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


199

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

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

  • save()
  • update()
  • saveOrUpdate()
  • saveOrUpdateCopy()
  • merge()
  • persist()

回答:


117

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

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

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

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

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

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

持続 としては、上述した、これは一過性のオブジェクトに使用されます。生成されたIDは返しません。


22
これを答えとして受け入れたいのですが、1つ不明な点があります。save()はupdate()にフォールバックするため、そのようなアイテムが存在する場合、実際にはsaveOrUpdate()とどのように違うのですか?
ヘンリックポール、

それがどこに指定されている場合、その保存はデタッチされたインスタンスで機能しますか?
jrudolph 2008年

2
マージ/永続化の説明が一時的なオブジェクトでのみ重要である場合、これは非常に意味があり、休止状態の使用方法に適合します。また、マージはある種の整合性チェックのために追加のフェッチを行うように見えるため、マージは更新に比べてパフォーマンスに制限があることがよくあります。
マーティンデールライネス2009年

1
以下のjrudolphの答えはより正確です。
azerole 2012

2
hibernateはおそらくオブジェクトがどの状態にあるか知っているので、プログラムを作成するときに手動でこれを行わなければならないのはなぜですか。保存メソッドは1つだけです。
masterxilo 2014年

116
╔══════════════╦═══════════════════════════════╦════════════════════════════════╗
    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()         
                                                                             
╚══════════════╩═══════════════════════════════╩════════════════════════════════╝

update一時的なオブジェクトは問題ありませんが、例外は発生しませんでした。
GMsoF 2018年

私が知っていることは、どちらの方法でも一時的な問題を解決することはできません。私は、一戸建てと永続的な違いがあると思います。訂正してください。
Ram

ここには多くのエラーがあります...例:1)「save()」は「接続されたオブジェクト」を返さず、「id」を返します。2)「persist()」が「id」を設定することは保証されておらず、「オブジェクトをDBに永続化する」こともできません。...
Eugen Labun

67
  • 永続化と保存の微妙な違いについては、Hibernateフォーラムを参照してください。違いは、INSERTステートメントが最終的に実行される時間です。以来、保存が識別子を返しません、INSERT文は関係なく、(一般的に悪いことである)トランザクションの状態を即座に実行する必要があります。Persistは、識別子を割り当てるためだけに、現在実行中のトランザクションの外部でステートメントを実行しません。Save / Persistはどちらも一時的なインスタンスで機能します。つまり、まだ識別子が割り当てられていないインスタンスであり、DBに保存されません。

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


独自のIDジェネレーターがある場合でも、ステートメントの挿入は行われません
kommradHomer

したがって、デタッチされたオブジェクトの場合、マージは選択をトリガーしますが、更新はトリガーしませんか?
2014年

1
save() - If an INSERT has to be executed to get the identifier, then this INSERT happens immediately, no matter if you are inside or outside of a transaction. This is problematic in a long-running conversation with an extended Session/persistence context.挿入がセッション外でどのように発生するのか、そしてなぜそれが悪いのか教えてください。
Erran Morad 14

免責事項:休止状態を長い間使用していません。IMOの問題はこれです。save()の署名とコントラクトでは、saveが新しいオブジェクトの識別子を返す必要があります。選択したID生成戦略に応じて、IDは値がINSERT編集されるときにDBによって生成されます。したがって、それらの場合、識別子を生成せずにすぐに識別子を返すことはできません。生成するには、INSERT 今すぐ実行する必要があります。長期実行トランザクションは現在実行されておらず、コミット時にのみ実行されるため、INSERT今実行する唯一の方法は、txの外部で実行することです。
jrudolph 2014

12

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

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

私たち全員が遭遇する問題はごくまれであり、それらをもう一度見ると、私たちはこれを解決したことを知っていますが、その方法を思い出せません。

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

            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セッションを閉じると、操作している永続オブジェクトが切り離されます。つまり、データはまだアプリケーションのメモリにありますが、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();

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

また、Java Persistence API(JPA)には、デタッチされたオブジェクトと再アタッチされたオブジェクトの概念がなく、EntityManager.persist()とEntityManager.merge()を使用することに注意することも重要です。

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

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


オリジナルは入手できないため、webarchiveに関する記事へのリンク:web.archive.org/web/20160521091122/http
//www.stevideter.com

5

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

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

long s = session.save(k);

この同じケースでpersist()は、クライアントに値を返すことはありません。


5

すべての休止状態の保存方法の違いを示す良い例を見つけました。

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

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

保存する()

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

persist()

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

saveOrUpdate()

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

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

更新()

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

マージ()

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

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


3

この記事で説明したように、ほとんどの場合はJPAメソッドを使用しupdate、バッチ処理タスクにはを使用する必要があります。

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

  • 一時的(新規)
  • マネージド(永続)
  • 戸建
  • 削除(削除)

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

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

ここに画像の説明を入力してください

Hibernate SessionはすべてのJPA EntityManagerメソッドを実装しsavesaveOrUpdateおよびのようないくつかの追加のエンティティ状態遷移メソッドを提供しますupdate

ここに画像の説明を入力してください

持続する

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

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

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

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が一次キャッシュとして知られている理由です。

を呼び出すpersistと、エンティティは現在実行中の永続コンテキストにのみアタッチされ、INSERTは、flushが呼び出される。

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

保存する

Hibernate固有のsaveメソッドはJPAよりも古く、Hibernateプロジェクトの開始以来利用可能でした。

このsaveメソッドSaveOrUpdateEventは、DefaultSaveOrUpdateEventListenerHibernateイベントリスナーによって処理されるをトリガーします。したがって、この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メソッド呼び出しと同じです。ただし、とは異なりpersistsaveメソッドはエンティティ識別子を返します。

詳しくは、こちらの記事をご覧ください。

更新

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

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

updateメソッドがどのように機能するかを確認するために、Bookエンティティを1つのトランザクションで永続化する次の例を検討し、エンティティが分離された状態にある間にそれを変更し、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は常に実行されます。これを防ぐには、フェッチし@SelectBeforeUpdateSELECTステートメントをトリガーするHibernateアノテーションを使用できます。loaded stateは、使用して、ダーティチェックメカニズムで。

したがって、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

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

SaveOrUpdate

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

このsaveOrUpdateメソッドSaveOrUpdateEventは、DefaultSaveOrUpdateEventListenerHibernateイベントリスナーによって処理されるをトリガーします。したがって、この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永続コンテキストが既に次の例のように同一の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番目に、渡したものと同じ識別子のエンティティがEntityManager既に含まれており、永続コンテキストが同じエンティティの2つの表現を保持できないためです。Bookupdate

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にはmerge、JPAが提供EntityManagerし、Hibernate Sessionも継承したメソッドを使用する必要があります。

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

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

mergeメソッドがどのように機能するかを確認するためにBook、1つのトランザクションでエンティティを永続化する次の例を検討します。次に、エンティティが分離された状態にある間にエンティティを変更し、分離さ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

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

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

このため、update現在実行中の永続コンテキストにすでにアタッチされているエンティティ参照がないこと、および切り離されたエンティティが変更されていることを確認してから使用することをお勧めします。

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

結論

エンティティを永続化するには、JPA persistメソッドを使用する必要があります。分離されたエンティティの状態をコピーするにmergeは、優先する必要があります。このupdateメソッドは、バッチ処理タスクにのみ役立ちます。savesaveOrUpdateにちょうど別名であるupdate、あなたは、おそらくすべてでそれらを使用しないでください。

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

詳しくは、こちらの記事をご覧ください。


2

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

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


1

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


11
以下の答えが正しくない理由を教えてください。
Erran Morad 14

0

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

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

ハイバネートの保存方法は絶対に使用しないでください。それも冬眠に存在することを忘れてください!

持続する

誰もが説明したように、Persistは基本的にエンティティを「一時的な」状態から「管理された」状態に移行します。この時点で、slushまたはcommitによって挿入ステートメントを作成できます。ただし、エンティティは引き続き「管理」状態のままです。フラッシュしても変わりません。

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

エンティティを立ち退かせようとするときに、楽しみが始まります。

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

マージと更新

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

デタッチドが一種の「オフライン」状態を意味するという事実を理解してください。管理とは「オンライン」状態を意味します。

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

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();

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

背後では、mergeは選択クエリを発生させ、基本的にアタッチされた状態のエンティティのコピーを返します。以下のコードを確認してください。

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();

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

Updateを適用すると、実際には更新がマージのようなエンティティのコピーをもたらさないため、同じようにうまくいきます。

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 like mergeのSQLクエリを発生させていないことがわかります。

削除する

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

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

上記の説明で疑問が明らかになったといいのですが。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.