1. 序章

このチュートリアルでは、 Session インターフェースのいくつかの方法の違いについて説明します: save persist update merge 、およびsaveOrUpdate

これはHibernateの概要ではなく、構成、オブジェクトリレーショナルマッピング、およびエンティティインスタンスの操作の基本をすでに知っている必要があります。 Hibernateの紹介記事については、 Hibernate 4 withSpringのチュートリアルにアクセスしてください。

2. 永続コンテキストの実装としてのセッション

Session インターフェースには、最終的にデータベースにデータを保存するいくつかのメソッドがあります。 、およびsaveOrUpdate。 これらのメソッドの違いを理解するには、まず、永続コンテキストとしての Session の目的と、Sessionに関連するエンティティインスタンスの状態の違いについて説明する必要があります。

また、APIメソッドが部分的に重複する原因となったHibernateの開発履歴も理解する必要があります。

2.1. エンティティインスタンスの管理

オブジェクトリレーショナルマッピング自体とは別に、Hibernateが解決する問題の1つは、実行時にエンティティを管理する問題です。 「永続コンテキスト」の概念は、この問題に対するHibernateのソリューションです。 永続コンテキストは、セッション中にデータベースにロードまたは保存したすべてのオブジェクトのコンテナーまたは第1レベルのキャッシュと考えることができます。

セッションは論理トランザクションであり、境界はアプリケーションのビジネスロジックによって定義されます。 永続コンテキストを介してデータベースを操作し、すべてのエンティティインスタンスがこのコンテキストにアタッチされている場合、セッション中にやり取りするデータベースレコードごとに、常に単一のエンティティインスタンスが必要です。

Hibernateでは、永続コンテキストはorg.hibernate.Sessionインスタンスで表されます。 JPAの場合は、javax.persistence.EntityManagerです。 HibernateをJPAプロバイダーとして使用し、 EntityManager インターフェースを介して操作する場合、このインターフェースの実装は基本的に、基盤となるSessionオブジェクトをラップします。 ただし、Hibernate Session は、より多くの可能性を備えたより豊富なインターフェイスを提供するため、Sessionを直接操作すると便利な場合があります。

2.2. エンティティインスタンスの状態

アプリケーション内のエンティティインスタンスは、Session永続コンテキストに関連する3つの主要な状態のいずれかに表示されます。

  • 一時的 —このインスタンスは、に接続されていません。 セッション。 このインスタンスには、データベースに対応する行がありません。 これは通常、データベースに保存するために作成した新しいオブジェクトです。
  • persistent —このインスタンスは一意のSessionオブジェクトに関連付けられています。 Session をデータベースにフラッシュすると、このエンティティはデータベースに対応する一貫したレコードを持つことが保証されます。
  • detached —このインスタンスはかつて Session に接続されていましたが( persistent 状態)、現在は接続されていません。 インスタンスをコンテキストから削除したり、セッションをクリアまたはクローズしたり、インスタンスをシリアル化/逆シリアル化プロセスにかけたりすると、インスタンスはこの状態になります。

これは、状態遷移を発生させるSessionメソッドに関するコメント付きの簡略化された状態図です。

エンティティインスタンスがpersistent状態の場合、このインスタンスのマップされたフィールドに加えたすべての変更は、Sessionをフラッシュするときに対応するデータベースレコードとフィールドに適用されます。 persistent インスタンスは「オンライン」ですが、 detached インスタンスは「オフライン」であり、変更が監視されていません。

つまり、 persistent オブジェクトのフィールドを変更する場合、これらを取得するために save update、またはこれらのメソッドを呼び出す必要はありません。データベースへの変更。 必要なのは、トランザクションをコミットするか、セッションをフラッシュするか、セッションを閉じることだけです。

2.3. JPA仕様への準拠

Hibernateは最も成功したJavaORM実装でした。 そのため、Hibernate APIはJava永続性API(JPA)の仕様に大きな影響を与えました。 残念ながら、多くの違いもあり、いくつかは大きなもので、いくつかはもっと微妙なものでした。

JPA標準の実装として機能するには、HibernateAPIを改訂する必要がありました。 EntityManager インターフェイスに一致させるために、Sessionインターフェイスにいくつかのメソッドが追加されました。 これらの方法は、元の方法と同じ目的を果たしますが、仕様に準拠しているため、いくつかの違いがあります。

3. 操作の違い

すべてのメソッド( persist save update merge saveOrUpdate )対応するSQL UPDATEまたはINSERTステートメントがすぐに生成されないようにします。 データベースへのデータの実際の保存は、トランザクションをコミットするか、セッションをフラッシュするときに行われます。

前述のメソッドは、基本的に、ライフサイクルに沿って異なる状態間でエンティティインスタンスを遷移させることにより、エンティティインスタンスの状態を管理します。

例として、単純な注釈マップエンティティPersonを使用します。

@Entity
public class Person {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    // ... getters and setters

}

3.1. 永続化

persist メソッドは、永続コンテキストに新しいエンティティインスタンスを追加することを目的としています。 インスタンスをtransientからpersistent状態に移行します。

通常、データベースにレコードを追加する(エンティティインスタンスを永続化する)場合に呼び出します。

Person person = new Person();
person.setName("John");
session.persist(person);

persist メソッドを呼び出した後はどうなりますか? personオブジェクトがtransientからpersistent状態に移行しました。 オブジェクトは現在永続コンテキストにありますが、まだデータベースに保存されていません。 INSERT ステートメントの生成は、トランザクションをコミットしたとき、またはセッションをフラッシュまたはクローズしたときにのみ発生します。

persistメソッドにはvoidリターン型があることに注意してください。 渡されたオブジェクトを「インプレース」で操作し、状態を変更します。 person 変数は、実際の永続化されたオブジェクトを参照します。

このメソッドは、後でSessionインターフェイスに追加されます。 この方法の主な差別化機能は、JSR-220仕様(EJB永続性)に準拠していることです。 このメソッドのセマンティクスは仕様で厳密に定義されており、基本的にtransientインスタンスはpersistent になります(操作は casino =PERSISTとのすべての関係にカスケードされます)。 またはcascade= ALL ):

  • インスタンスがすでにpersistentである場合、この呼び出しはこの特定のインスタンスには影響しません(ただし、 casino =PERSISTまたはcascade=ALLとの関係にカスケードされます。 ])。
  • インスタンスがdetachedの場合、このメソッドを呼び出すか、セッションをコミットまたはフラッシュするときに、例外が発生します。

ここでは、インスタンスの識別子に関係するものは何もないことに注意してください。 仕様には、ID生成戦略に関係なく、IDがすぐに生成されるとは記載されていません。 persist メソッドの仕様により、実装はステートメントを発行して、コミットまたはフラッシュ時にIDを生成できます。 このメソッドを呼び出した後、idは必ずしもnull以外になるとは限らないため、これに依存するべきではありません。

すでにpersistentインスタンスでこのメソッドを呼び出すことができますが、何も起こりません。 ただし、 detached インスタンスを永続化しようとすると、実装は例外をスローします。 次の例では、エンティティを永続化し、コンテキストから削除して切り離しにし、永続化を試みます。 ] また。 session.persist()を2回呼び出すと例外が発生するため、次のコードは機能しません。

Person person = new Person();
person.setName("John");
session.persist(person);

session.evict(person);

session.persist(person); // PersistenceException!

3.2. 保存

save メソッドは、JPA仕様に準拠していない「元の」Hibernateメソッドです。

その目的は基本的にpersistと同じですが、実装の詳細が異なります。 このメソッドのドキュメントには、「最初に生成された識別子を割り当てる」というインスタンスの永続化が厳密に記載されています。 このメソッドは、この識別子のSerializable値を返します。

Person person = new Person();
person.setName("John");
Long id = (Long) session.save(person);

すでに永続化されているインスタンスを保存する効果は、persistの場合と同じです。 違いは、detachedインスタンスを保存しようとしたときに発生します。

Person person = new Person();
person.setName("John");
Long id1 = (Long) session.save(person);

session.evict(person);
Long id2 = (Long) session.save(person);

id2変数はid1とは異なります。 detached インスタンスでの保存呼び出しは、新しい persistent インスタンスを作成し、それに新しい識別子を割り当てます。これにより、コミットまたはフラッシュ時にデータベースに重複レコードが作成されます。

3.3. マージ

merge メソッドの主な目的は、persistentエンティティインスタンスをdetachedエンティティインスタンスからの新しいフィールド値で更新することです。

たとえば、呼び出し元へのIDによってJSONシリアル化されたオブジェクトを取得するメソッドと、呼び出し元からこのオブジェクトの更新バージョンを受け取るメソッドを備えたRESTfulインターフェイスがあるとします。 このようなシリアル化/逆シリアル化を通過したエンティティは、detached状態で表示されます。

このエンティティインスタンスを逆シリアル化した後、永続コンテキストから persistent エンティティインスタンスを取得し、このdetachedインスタンスからの新しい値でそのフィールドを更新する必要があります。 したがって、mergeメソッドはまさにそれを行います。

  • 渡されたオブジェクトから取得したIDでエンティティインスタンスを検索します(永続コンテキストからの既存のエンティティインスタンスが取得されるか、データベースからロードされた新しいインスタンスのいずれか)
  • 渡されたオブジェクトからこのインスタンスにフィールドをコピーします
  • 新しく更新されたインスタンスを返します

次の例では、保存されたエンティティをコンテキストから evict (デタッチ)し、 name フィールドを変更してから、 merge detached ] 実在物:

Person person = new Person(); 
person.setName("John"); 
session.save(person);

session.evict(person);
person.setName("Mary");

Person mergedPerson = (Person) session.merge(person);

mergeメソッドはオブジェクトを返すことに注意してください。 引数として渡したpersonオブジェクトではなく、永続コンテキストにロードして更新したmergedPersonオブジェクトです。 これらは2つの異なるオブジェクトであり、通常はpersonオブジェクトを破棄する必要があります。

persist メソッドと同様に、 merge メソッドは、信頼できる特定のセマンティクスを持つようにJSR-220によって指定されています。

  • エンティティが切り離されたの場合、既存の永続的エンティティにコピーされます。
  • エンティティがtransientの場合、新しく作成されたpersistentエンティティにコピーされます。
  • この操作は、 casino =MERGEまたはcascade=ALLマッピングのすべての関係に対してカスケードされます。
  • エンティティがpersistentの場合、このメソッド呼び出しはエンティティに影響を与えません(ただし、カスケードは引き続き行われます)。

3.4. 更新

persistおよびsaveと同様に、updateメソッドは「元の」Hibernateメソッドです。 そのセマンティクスは、いくつかの重要な点で異なります。

  • 渡されたオブジェクトに作用します(その戻りタイプは void です)。 update メソッドは、渡されたオブジェクトをdetachedからpersistent状態に遷移させます。
  • このメソッドにtransientエンティティを渡すと、このメソッドは例外をスローします。

次の例では、オブジェクトを保存し、コンテキストから evict (デタッチ)してから、その name を変更し、updateを呼び出します。 ]。 updatepersonオブジェクト自体で行われるため、update操作の結果を別の変数に入れないことに注意してください。 基本的に、既存のエンティティインスタンスを永続コンテキストに再アタッチします。これは、JPA仕様では許可されていないことです。

Person person = new Person();
person.setName("John");
session.save(person);
session.evict(person);

person.setName("Mary");
session.update(person);

transientインスタンスでupdateを呼び出そうとすると、例外が発生します。 以下は機能しません。

Person person = new Person();
person.setName("John");
session.update(person); // PersistenceException!

3.5. SaveOrUpdate

このメソッドはHibernateAPIにのみ表示され、標準化された対応するメソッドはありません。 update と同様に、インスタンスの再接続にも使用できます。

実際、updateメソッドを処理する内部DefaultUpdateEventListenerクラスは、 DefaultSaveOrUpdateListener のサブクラスであり、一部の機能をオーバーライドするだけです。 saveOrUpdate メソッドの主な違いは、transient インスタンスに適用されたときに例外をスローせず、代わりにこのtransientインスタンスを作成することです。永続的。 次のコードは、新しく作成されたPersonのインスタンスを永続化します。

Person person = new Person();
person.setName("John");
session.saveOrUpdate(person);

このメソッドは、オブジェクトが一時的または切り離されたであるかどうかに関係なく、オブジェクトを永続的にするためのユニバーサルツールと考えることができます。

4. 何を使用しますか?

特別な要件がない場合は、persistおよびmergeメソッドが標準化されており、JPA仕様に準拠しているため、これらのメソッドを使用する必要があります。

また、別の永続性プロバイダーに切り替えることにした場合に備えて、移植性もあります。 ただし、「元の」Hibernateメソッド、 save update、、およびsaveOrUpdateほど有用ではないように見える場合があります。

5. 結論

この記事では、実行時の永続エンティティの管理に関連して、さまざまなHibernateSessionメソッドの目的について説明しました。 これらのメソッドがライフサイクルを通じてエンティティインスタンスをどのように移行するか、およびこれらのメソッドの一部が機能を複製する理由を学びました。

この記事のソースコードは、GitHubから入手できます。