persist、save、merge、updateの違いは何ですか? どちらを使うべきですか?

JPAとHibernateは、新しいエンティティを永続化し、既存のエンティティを更新するための異なる方法を提供します。 JPAのpersistとmergeとHibernateのsaveとupdateメソッドのどちらかを選択できます。

同じことを行う2つのメソッドの2つのペアがあるようです。 Persistメソッドとsaveメソッドを使用して新しいエンティティを格納し、mergeメソッドとupdateメソッドを使用して、デタッチされたエンティティの変更をデー そのため、多くの開発者がこれらのメソッドのどれを使用すべきか疑問に思っています。 これらの方法の詳細と小さな違いを詳しく見てみましょう。

Hibernateの隠された実装の詳細についてのフィードバックと素晴らしい洞察を提供したSteve Ebersole(リード開発者–Hibernate ORM)に感謝します!

エンティティ状態遷移

これらの4つのメソッドの詳細に入る前に、JPAのエンティティライフサイクル状態を簡単に紹介する必要があります。

エンティティが現在の永続性コンテキストにアタッチされている場合、ライフサイクル状態が管理されます。 これは、データベースレコードにマップされていることを意味します。 永続化プロバイダは、すべての変更を伝播するために必要なSQL INSERT文およびUPDATE文を生成します。 管理対象エンティティは、第1レベルのキャッシュにも格納されます。

新しいエンティティを作成すると、一時的な状態になります。 現在の永続性コンテキストにアタッチするまで、この状態のままです。 次のセクションでは、JPAのpersistとHibernateのsaveメソッドを使用してそれを行う方法を示します。 エンティティが一時状態にある限り、エンティティはデータベースレコードにマップされず、永続性コンテキストによって管理されません。

detached lifecycle状態のエンティティは、永続コンテキストによって管理されなくなりました。 これは、永続コンテキストを閉じたか、エンティティを現在のコンテキストから明示的にデタッチしたためです。 この記事の後半では、JPAのmergeメソッドとHibernateのupdateメソッドを使用してこれらのエンティティを再接続する方法の詳細について説明します。

最後のライフサイクル状態が削除されます。 これらのエンティティは、削除のスケジュールを設定する前に、以前は管理されていた状態でした。 エンティティを削除することはこの投稿の範囲外なので、私はそれについてあまり多くの詳細に入ることはありません。 EntityManagerインターフェイスでremoveメソッドを呼び出すことで、エンティティの削除をスケジュールできます。

persistまたはsaveを使用した新しいエンティティの永続化

新しいエンティティオブジェクトを作成すると、一時的なライフサイクル状態になります。 データベースレコードはマップされません。

Author a = new Author();a.setFirstName("Thorben");a.setLastName("Janssen");

エンティティを永続化コンテキストにアタッチして、管理され、データベースに永続化されるようにする必要があります。 JPAのpersistまたはHibernateのsaveメソッドを使用してそれを行うことができます。 どちらの方法も同じように見えますが、いくつかの違いがあります。

プロプライエタリAPI

最も明白な違いは、JPA仕様でpersistメソッドが定義されていることです。 すべてのJPA実装で使用できます。 一方、saveメソッドはHibernate固有です。 したがって、他のJPA実装では使用できません。しかし、これは、HibernateをEclipse LinkやOpenJPAのような別のJPA実装に置き換えることができる場合にのみ関連します。

戻り値の型とSQL文の実行

これらの2つのメソッドのもう一つの明白な違いは、戻り値の型です。 JPAのpersistメソッドはvoidを返し、Hibernateのsaveメソッドはエンティティの主キーを返します。

これは、特にHibernateのJavadocとJPA仕様を詳しく見てみると、大きな違いのように見えるかもしれません:

  • HibernateのsaveメソッドのJavadocは、最初に主キー値を生成すると述べています:

    指定された一時インスタンスを永続化し、最初に生成された識別子を割り当てます。
    Javadocセッション。保存(エンティティ)

  • JPA仕様にはこれに関する情報はありません。 主キー値をいつ割り当てる必要があるかは定義されていません。 したがって、永続化プロバイダは、persistメソッドの呼び出しと永続化コンテキストのフラッシュの間の任意の時点でこれを行うことができます。

ほとんどの場合、saveメソッドまたはpersistメソッドを呼び出しても違いはありません。 Hibernateは、エンティティクラスの名前と主キー値を使用して、エンティティを最初のレベルのキャッシュに格納します。 したがって、persistメソッドを実行するときには、主キー値が必要です。

ほとんどすべての状況で、Hibernateはすぐに主キー値を生成し、persistまたはsaveメソッドを呼び出すときに必要に応じてSQLステートメントをトリガーします。

しかし、ID戦略を使用して、アクティブなトランザクションなしまたはFlushModeを使用してエンティティを永続化しようとすると、そうではありません。マニュアル。 これらの状況のいずれかでpersistメソッドを呼び出すと、HibernateはSQL INSERTステートメントの実行を遅延させ、一時的な主キー値を作成します。 しかし、saveメソッドを呼び出すと、HibernateはすぐにSQL INSERTステートメントを実行し、データベースから主キー値を取得します。

その後、saveメソッドの戻り値として取得できます。

Author a = new Author();a.setFirstName("Thorben");a.setLastName("Janssen");Long id = (Long) em.unwrap(Session.class).save(a);

または、jpaのpersistメソッドを使用する場合は、管理エンティティの主キー属性のgetterメソッドを呼び出すことができます。

Author a = new Author();a.setFirstName("Torben");a.setLastName("Janssen");em.persist(a);Long id = a.getId();

hibernateは、persistメソッドまたはsaveメソッドを呼び出すときに同じSQLステートメントを実行します。 それはあなたの主キーの生成戦略に依存します:

Not generated

主キー値をプログラムで設定した場合、たとえば自然識別子に設定すると、Hibernateは永続コンテキストをフラッシュするときにSQL INSERT文のみを実行します。

14:08:34,979 INFO TestPersistSaveMerge:237 - Save entity14:08:35,052 INFO TestPersistSaveMerge:240 - Commit transaction14:08:35,123 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)
ユーチューブ動画

ID戦略で生成された

ID戦略を使用して主キー値を生成する場合、hibernateはsaveまたはpersistメソッドを呼び出してデータベースから主キー値を取得するときにINSERTステー

14:09:28,264 INFO TestPersistSaveMerge:237 - Save entity14:09:28,336 DEBUG SQL:92 - insert into Author (firstName, lastName, version) values (?, ?, ?)14:09:28,354 INFO TestPersistSaveMerge:240 - Commit transaction
ユーチューブ動画

シーケンス戦略

で生成され、シーケンスを使用する場合、HibernateはSQL SELECTステートメントを実行してデータベースシーケンスから次の値を取得します。 その後、Hibernateは永続コンテキストをフラッシュするまでINSERT文を遅延させます。 この例では、トランザクションがコミットされたときにフラッシュが発生します。

14:10:27,994 INFO TestPersistSaveMerge:237 - Save entity14:10:28,002 DEBUG SQL:92 - select nextval ('hibernate_sequence')14:10:28,042 INFO TestPersistSaveMerge:240 - Commit transaction14:10:28,096 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)

テーブル戦略で生成された

主キーテーブルの行レベルロックが必要で、スケールがうまくいかないため、テーブル戦略を使用しないでください。 とにかくこの戦略を使用すると、HibernateはSQL SELECTステートメントを実行してデータベースから次の主キー値を取得し、新しい値をデータベーステーブルに書き込みます。 永続コンテキストがフラッシュされるまで、新しいエンティティのSQL INSERTステートメントの実行を遅延させます。

14:11:17,368 INFO TestPersistSaveMerge:237 - Save entity14:11:17,482 DEBUG SQL:92 - select tbl.next_val from hibernate_sequences tbl where tbl.sequence_name=? for update of tbl14:11:17,531 DEBUG SQL:92 - insert into hibernate_sequences (sequence_name, next_val) values (?,?)14:11:17,534 DEBUG SQL:92 - update hibernate_sequences set next_val=? where next_val=? and sequence_name=?14:11:17,584 INFO TestPersistSaveMerge:240 - Commit transaction14:11:17,655 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)

どちらを選ぶ?

jpa仕様とHibernate独自のメソッドのJavadocの間にはいくつかの違いがあるため、saveメソッドとpersistメソッドの動作が異なることが予想されます。

しかし、内部実装を見ると、これらの違いのほとんどすべてが消えます。 残りの唯一のものは、Hibernateが主キーの取得、メソッドの戻り値の型、および他のJPA実装によるサポートを遅らせる可能性のある2つのコーナーケースです。

ほとんどのアプリケーションでは、生成された主キー値をHibernateのsaveメソッドの戻り値の型として、または主キー属性のgetterメソッドから取得しても違いはあ 拡張永続コンテキストを使用せず、アクティブなトランザクションですべてのデータベース操作を実行する限り、JPAのpersistメソッドを使用することをお勧

デタッチされたエンティティの更新

現在の永続性コンテキストを閉じるか、EntityManagerインターフェイスのclearまたはdetachメソッドを呼び出してエンティティ つまり、第1レベルのキャッシュには保存されなくなり、Hibernateは適用された変更をデータベースにレプリケートしません。

HibernateのupdateまたはJPAのmergeメソッドを使用して、分離されたエンティティを永続コンテキストに関連付けることができます。 これが完了すると、Hibernateはentity属性値に基づいてデータベースを更新します。

updateメソッドとmergeメソッドの効果は同じように見えますが、次のセクションで見るように、重要な違いがあります。

JPAのmergeメソッド

jpaのmergeメソッドは、分離されたエンティティの状態を同じエンティティのマネージインスタンスにコピーします。 したがって、HibernateはSQL SELECTステートメントを実行して、データベースからマネージエンティティを取得します。 永続コンテキストにエンティティのマネージインスタンスが既に含まれている場合、Hibernateは代わりに既存のインスタンスを使用します。 次に、すべての属性値をマネージエンティティにコピーし、呼び出し元に返します。

Author managedAuthor = em.merge(a);

SQL文のロギングを有効にすると、実行されたSELECT文とUPDATE文がログ出力に表示されます。

11:37:21,172 DEBUG SQL:92 - select books0_.bookId as bookId1_2_0_, books0_.authorId as authorId2_2_0_, book1_.id as id1_1_1_, book1_.fk_author as fk_autho6_1_1_, book1_.format as format2_1_1_, book1_.publishingDate as publishi3_1_1_, book1_.title as title4_1_1_, book1_.version as version5_1_1_, author2_.id as id1_0_2_, author2_.firstName as firstNam2_0_2_, author2_.lastName as lastName3_0_2_, author2_.version as version4_0_2_ from BookAuthor books0_ inner join Book book1_ on books0_.authorId=book1_.id left outer join Author author2_ on book1_.fk_author=author2_.id where books0_.bookId=?11:37:21,180 INFO TestPersistSaveMerge:82 - Before commit11:37:21,182 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?

Hibernateが次回の永続コンテキストをフラッシュすると、そのダーティチェックメカニズムはすべての管理エンティティをチェックします。 Merge操作でエンティティ属性値が変更されたことが検出されると、必要なSQL UPDATEステートメントがトリガーされます。

JPAのmergeメソッドを使用するときに知っておく必要がある重要な詳細が1つあります。 Hibernateは、デタッチされたエンティティの属性値を管理エンティティにコピーします。 これにより、現在のセッション内でこのエンティティに対して実行した変更が上書きされます。

Hibernateのupdateメソッド

HIBERNATEのupdateメソッドはSQL SELECTステートメントをトリガーしません。 エンティティを現在の永続性コンテキストにアタッチするだけです。 JPAのmergeメソッドとは対照的に、updateメソッドを呼び出しても変更を失うことはありません。 永続化コンテキストに更新するエンティティのマネージインスタンスが既に含まれている場合は、例外がスローされます。

em.unwrap(Session.class).update(a);

Hibernateが次のフラッシュを実行すると、ダーティチェックは実行されません。 Hibernateはデータベースからエンティティの最新バージョンを読み取らなかったため、これは不可能です。 再接続されたエンティティに対してSQL UPDATEステートメントを実行するだけです。

11:38:28,151 INFO TestPersistSaveMerge:121 - Before commit11:38:28,153 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?

ダーティチェックが欠落していると、エンティティと対応するデータベースレコードに同じ値が含まれている場合、不要なSQL UPDATE文が発生します。 これは、DBAがデータベース表の更新トリガーを登録した場合に問題になる可能性があります。 このような状況では、@SelectBeforeUpdateを使用してエンティティに注釈を付けることができます。

@Entity@SelectBeforeUpdatepublic class Author { ... }

は、SQL UPDATEステートメントを生成する前にエンティティを選択し、ダーティチェックを実行するようにHibernateに指示します。 ログ出力でわかるように、updateメソッドの動作はJPAのmergeメソッドに似ています。

19:08:16,530 INFO TestPersistSaveMerge:121 - Before commit19:08:16,531 DEBUG SQL:92 - select author_.id, author_.firstName as firstNam2_0_, author_.lastName as lastName3_0_, author_.version as version4_0_ from Author author_ where author_.id=?19:08:16,592 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?

しかし、2つの方法の間には大きな違いがあります。 Updateメソッドを呼び出すと、Hibernateはメソッドパラメータとして指定したエンティティのみを選択します。 しかし、JPAのmergeメソッドを呼び出すと、HibernateはCascadeTypeとのすべての関連付けも選択します。マージ。 したがって、エンティティの巨大なグラフを再接続する場合は、JPAのmergeメソッドを好む必要があります。

どちらを選ぶ?

この質問に対する一般的な答えはありません。 あなたが見てきたように、両方の方法には長所と短所があります。 Hibernateがsql UPDATE文をトリガーする前にエンティティを選択する必要がある場合は、特定のユースケースを決定する必要があります。 その場合は、エンティティグラフの深さと、提供されたフェッチ動作のパフォーマンスへの影響も考慮する必要があります。

マネージエンティティの更新

JpaとHibernateは、マネージエンティティの更新を非常に簡単にします。 たとえば、JPQLクエリまたはEntityManagerのfindメソッドで取得したため、エンティティがライフサイクル状態で管理されている場合は、エンティティ属性の値を変

em = emf.createEntityManager();em.getTransaction().begin();a = em.find(Author.class, a.getId());a.setFirstName("Thorben");log.info("Before commit");em.getTransaction().commit();em.close();

Hibernateが永続コンテキストをフラッシュすることを決定すると、ダーティチェックメカニズムは変更を検出し、必要なSQL UPDATEステートメントを実行します。

11:41:49,178 DEBUG SQL:92 - select author0_.id as id1_0_0_, author0_.firstName as firstNam2_0_0_, author0_.lastName as lastName3_0_0_, author0_.version as version4_0_0_ from Author author0_ where author0_.id=?11:41:49,191 INFO TestPersistSaveMerge:335 - Before commit11:41:49,193 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?

あなたはする必要はありません、あなたはエンティティを更新した後、Hibernateのsaveメソッドを呼び出すべきではありません。 これにより、利点を提供せずに追加のSaveOrUpdateイベントがトリガーされます。 Hibernateが永続コンテキストをフラッシュすることを決定すると、必要なSQL UPDATEステートメントを実行する前に、すべての変更を検出するためにダーティチェック

You might also like

コメントを残す

メールアドレスが公開されることはありません。