Hibernateエンティティライフサイクル

1概要

すべてのHibernateエンティティは、当然のことながらフレームワーク内にライフサイクルがあります - それは一時的な、管理された、切り離された、または削除された状態のいずれかです。

概念的にも技術的にもこれらの状態を理解することはHibernateを適切に使用することができるために不可欠です。

エンティティを扱うさまざまなHibernateメソッドについて学ぶためには、https://www.baeldung.com/hibernate-save-persist-update-merge-saveorupdateを参照してください。

2ヘルパーメソッド

このチュートリアルでは、一貫していくつかのヘルパーメソッドを使用します。

  • __HibernateLifecycleUtil.getManagedEntities(session) - これを使用します

Session’s 内部ストアからすべての管理対象エンティティを取得する

  • __DirtyDataInspector.getDirtyEntities() - これを使用します

「ダーティ」とマークされたすべてのエンティティのリストを取得する方法

  • ____HibernateLifecycleUtil.queryCount(query) - 便利なメソッド

組み込みデータベースに対して count(** ) クエリを実行する方法

上記のヘルパーメソッドはすべて読みやすくするために静的にインポートされています。この記事の最後にリンクされているGitHubプロジェクトに、それらの実装があります。

3永続コンテキストについてのすべてです

エンティティライフサイクルのトピックに入る前に、まず、永続コンテキストについて理解する必要があります。

  • 簡単に言うと、永続コンテキストはクライアントコードとデータストアの間にあります。これは、永続データがエンティティに変換され、クライアントコードによって読み取られて変更される準備ができているステージング領域です。

理論的には、 __persistenceコンテキスト __はhttps://martinfowler.com/eaaCatalog/unitOfWork.html[Unit of Work]パターンの実装です。ロードされたすべてのデータを追跡し、そのデータの変更を追跡し、ビジネストランザクションの最後に最終的に変更をデータベースに同期させる責任があります。

JPA EntityManager およびHibernateの __Sessionは、 持続コンテキスト conceptの実装です。この記事では、持続コンテキストを表すためにHibernate Session__を使用します。

次に説明するように、Hibernateエンティティのライフサイクル状態は、エンティティが 永続コンテキスト とどのように関連しているかを説明します。

4管理対象エンティティ

  • 管理対象エンティティは、データベーステーブルの行** を表したものです(ただし、その行はまだデータベースに存在する必要はありません)。

これは現在実行中の Session によって管理され、 行われたすべての変更は自動的に追跡されデータベースに伝達されます

____Sessionは、データベースからエンティティをロードするか、切り離されたエンティティを再接続します。第5章で、独立したエンティティについて説明します。

明確にするためにいくつかのコードを見てみましょう。

このサンプルアプリケーションでは、 FootballPlayer クラスという1つのエンティティを定義しています。

起動時に、サンプルデータを使用してデータストアを初期化します。

+-------------------+-------+
| Name              |  ID   |
+-------------------+-------+
| Cristiano Ronaldo | 1     |
| Lionel Messi      | 2     |
| Gianluigi Buffon  | 3     |
+-------------------+-------+

最初にBuffonの名前を変更したいとしましょう - 彼のフルネーム __Gianluigi Buffon __の代わりにGigi Buffonを入れたいのです。

まず、__Sessionを取得して作業単位を開始する必要があります。

Session session = sessionFactory.openSession();

サーバー環境では、コンテキスト対応プロキシを介してコードに Session を挿入することができます。原則は変わりません。作業単位の業務トランザクションをカプセル化するために____Sessionが必要です。

次に、永続ストアからデータをロードするように Session に指示します。

assertThat(getManagedEntities(session)).isEmpty();

List<FootballPlayer> players = s.createQuery("from FootballPlayer").getResultList();

assertThat(getManagedEntities(session)).size().isEqualTo(3);

最初に Session を取得したとき、最初の assert ステートメントに示されているように、その永続コンテキストストアは空です。

次に、データベースからデータを取得し、そのデータのエンティティ表現を作成し、最後に使用するエンティティを返すクエリを実行します。

内部的には、 _ Sessionは永続コンテキストストアにロードされるすべてのエンティティを追跡します。私たちの場合、 Sessionの _ internalストアには、クエリの後に3つのエンティティが含まれます。

それでは、Gigiの名前を変更しましょう。

Transaction transaction = session.getTransaction();
transaction.begin();

FootballPlayer gigiBuffon = players.stream()
  .filter(p -> p.getId() == 3)
  .findFirst()
  .get();

gigiBuffon.setName("Gianluigi Buffon");
transaction.commit();

assertThat(getDirtyEntities()).size().isEqualTo(1);
assertThat(getDirtyEntities().get(0).getName()).isEqualTo("Gianluigi Buffon");

4.1. どのように動作しますか?

トランザクション commit() または flush() を呼び出すと、 Session は追跡リストから dirty エンティティを見つけて、その状態をデータベースに同期させます。

  • エンティティを変更したことを Session に通知するためのメソッドを呼び出す必要がないことに注意してください。これは管理対象エンティティであるため、すべての変更は自動的にデータベースに伝達されます。

管理対象エンティティーは常に永続エンティティーです。データベース行表現がまだ作成されていない場合でもデータベースIDを持っている必要があります。つまり、INSERTステートメントは作業単位の終わりを待っています。

下記の一時的エンティティに関する章を参照してください。

5独立したエンティティ

  • デタッチされたエンティティは、ID値がデータベース行に対応する通常のエンティティPOJO です。管理対象エンティティとの違いは、 永続コンテキスト__によって追跡されなくなったことです。

エンティティは、ロードに使用されていた Session が閉じられたとき、または Session.evict(entity) または Session.clear() が呼び出されたときに切り離されることがあります。

コードで見てみましょう。

FootballPlayer cr7 = session.get(FootballPlayer.class, 1L);

assertThat(getManagedEntities(session)).size().isEqualTo(1);
assertThat(getManagedEntities(session).get(0).getId()).isEqualTo(cr7.getId());

session.evict(cr7);

assertThat(getManagedEntities(session)).size().isEqualTo(0);

私たちの永続コンテキストはデタッチされたエンティティの変更を追跡しません。

cr7.setName("CR7");
transaction.commit();

assertThat(getDirtyEntities()).isEmpty();

Session.merge(entity)/Session.update(entity)はセッションを (再)アタッチすることができます _: _

FootballPlayer messi = session.get(FootballPlayer.class, 2L);

session.evict(messi);
messi.setName("Leo Messi");
transaction.commit();

assertThat(getDirtyEntities()).isEmpty();

transaction = startTransaction(session);
session.update(messi);
transaction.commit();

assertThat(getDirtyEntities()).size().isEqualTo(1);
assertThat(getDirtyEntities().get(0).getName()).isEqualTo("Leo Messi");

Session.merge() Session.update() の両方についての参照はhttps://www.baeldung.com/hibernate-save-persist-update-merge-saveorupdate[ここ]を参照してください。

5.1. アイデンティティーフィールドはすべて重要です

次のロジックを見てみましょう。

FootballPlayer gigi = new FootballPlayer();
gigi.setId(3);
gigi.setName("Gigi the Legend");
session.update(gigi);

上記の例では、通常の方法でコンストラクタを介してエンティティをインスタンス化しました。フィールドに値を入力し、IDを3に設定しました。これは、Gigi Buffonに属する永続データのIDに対応します。 update() を呼び出すと、別の persistence context からエンティティを読み込んだ場合とまったく同じ効果があります。

実際、____Sessionは、再接続されたエンティティがどこから発生したのかを区別しません。

HTMLフォームの値から独立したエンティティを構築することは、Webアプリケーションでは非常に一般的なシナリオです。

Session に関する限り、デタッチされたエンティティはID値が永続データに対応する単なるエンティティです。

上記の例は、デモ目的にのみ役立つことに注意してください。そして私たちは自分たちがしていることを正確に知る必要があります。そうでなければ、更新したいフィールドに値を設定しただけで残りの部分は変更されないままになるため、エンティティ全体でnull値になる可能性があります(したがって、事実上null)。

6. 一時的なエンティティ

  • 一時的なエンティティは、永続ストア内に表現がなく、** Session によって管理されない、単に entity オブジェクトです。

一時的なエンティティの典型的な例は、そのコンストラクタを介して新しいエンティティをインスタンス化することです。

一時的なエンティティを persistent にするには、 __Session.save(entity) または Session.saveOrUpdate(entity)を呼び出す必要があります。

FootballPlayer neymar = new FootballPlayer();
neymar.setName("Neymar");
session.save(neymar);

assertThat(getManagedEntities(session)).size().isEqualTo(1);
assertThat(neymar.getId()).isNotNull();

int count = queryCount("select count(** ) from Football__Player where name='Neymar'");

assertThat(count).isEqualTo(0);

transaction.commit();
count = queryCount("select count(** ) from Football__Player where name='Neymar'");

assertThat(count).isEqualTo(1);

Session.save(entity) を実行するとすぐに、エンティティにはID値が割り当てられ、 Session によって管理されるようになります。ただし、INSERT操作は作業単位の終わりまで延期される可能性があるため、データベースではまだ使用できない可能性があります。

7. 削除されたエンティティ

  • Session.delete(entity) ** が呼び出され、 __ Session __がエンティティを削除対象としてマークしている場合、エンティティは削除(削除)された状態にあります。

作業単位の終わりにDELETEコマンド自体が出されることがあります。

次のコードで見てみましょう。

session.delete(neymar);

assertThat(getManagedEntities(session).get(0).getStatus()).isEqualTo(Status.DELETED);

ただし、エンティティーは作業単位の終わりまで永続コンテキスト・ストアに留まることに注意してください。

8結論

永続コンテキストの概念は、Hibernateエンティティのライフサイクルを理解する上で中心的なものです。それぞれの状況を示すコード例を調べて、ライフサイクルを明確にしました。

いつものように、この記事で使われているコードはhttps://github.com/eugenp/tutorials/tree/master/persistence-modules/hibernate5[over GitHub]にあります。

前の投稿:Spring Bootで@JsonComponentを使う
次の投稿:JavaでK番目に大きい要素を見つける方法