JPA / Hibernate永続性コンテキスト

1。概要

Hibernateのような永続性プロバイダーは、永続性コンテキストを利用して、アプリケーションのエンティティライフサイクルを管理します。

このチュートリアルでは、永続コンテキストの紹介から始め、次にそれが重要である理由を説明します。最後に、トランザクションスコープの永続コンテキストと拡張スコープの永続コンテキストの違いを例を挙げて説明します。

2.永続コンテキスト

永続性コンテキストの公式定義を見てみましょう。

EntityManagerインスタンスは、永続コンテキストに関連付けられています。永続コンテキストは、永続エンティティIDに対して一意のエンティティインスタンスが存在するエンティティインスタンスのセットです。永続性コンテキスト内で、エンティティインスタンスとそのライフサイクルが管理されます。EntityManager APIは、永続エンティティインスタンスの作成と削除、主キーによるエンティティの検索、およびエンティティのクエリに使用されます。

上記のステートメントは、現時点では少し複雑に見えるかもしれませんが、先に進むにつれて完全に理にかなっています。永続コンテキストは、すべてのエンティティがデータベースからフェッチされるか、データベースに保存される第1レベルのキャッシュです。これは、アプリケーションと永続ストレージの間にあります。

永続コンテキストは、管理対象エンティティに加えられた変更を追跡します。トランザクション中に何かが変更された場合、エンティティはダーティとしてマークされます。トランザクションが完了すると、これらの変更は永続ストレージにフラッシュされます。

EntityManagerが永続コンテキストで私たちをやり取りすることができますインターフェイスです。EntityManagerを使用するときはいつでも、実際には永続コンテキストと対話しています

エンティティに加えられたすべての変更が永続ストレージを呼び出す場合、何回の呼び出しが行われるかを想像できます。永続ストレージの呼び出しにはコストがかかるため、これはパフォーマンスに影響を与えます。

3.永続コンテキストタイプ

永続コンテキストには、次の2つのタイプがあります。

  • トランザクションスコープの永続コンテキスト
  • 拡張スコープの永続コンテキスト

それぞれを見てみましょう。

3.1トランザクションスコープの永続コンテキスト

トランザクション永続コンテキストはトランザクションにバインドされます。トランザクションが終了するとすぐに、永続コンテキストに存在するエンティティが永続ストレージにフラッシュされます。

トランザクション内で操作を実行すると、EntityManagerは永続コンテキストをチェックします存在する場合は、それが使用されます。それ以外の場合は、永続コンテキストが作成されます。

デフォルトの永続コンテキストタイプPersistenceContextType.TRANSACTIONです。伝えるためにEntityManagerをトランザクション永続コンテキストを使用するために、我々は単純で、それに注釈を付ける@PersistenceContext

@PersistenceContext private EntityManager entityManager;

3.2拡張スコープの永続コンテキスト

拡張永続コンテキストは、複数のトランザクションにまたがることができます。トランザクションなしでエンティティを永続化することはできますが、トランザクションなしでエンティティをフラッシュすることはできません。

伝えるためにEntityManagerを拡張スコープの永続コンテキストを使用するために、我々は、適用する必要があるタイプの属性@PersistenceContextを

@PersistenceContext(type = PersistenceContextType.EXTENDED) private EntityManager entityManager;

ステートレスセッションBeanでは、あるコンポーネントの拡張永続コンテキストは、別のコンポーネントの永続コンテキストを完全に認識していませんこれは、両方が同じトランザクションにある場合でも当てはまります。

トランザクションで実行されているコンポーネントAのメソッドでエンティティを永続化するとします。次に、コンポーネントBのメソッドを呼び出します。コンポーネントBのメソッド永続化コンテキストでは、コンポーネントAのメソッドで以前に永続化したエンティティは見つかりません。

4.永続コンテキストの例

永続コンテキストについて十分に理解できたので、次に例を見てみましょう。トランザクション永続コンテキストと拡張永続コンテキストを使用して、さまざまなユースケースを作成します。

まず、サービスクラスTransctionPersistenceContextUserServiceを作成しましょう。

@Component public class TransctionPersistenceContextUserService { @PersistenceContext private EntityManager entityManager; @Transactional public User insertWithTransaction(User user) { entityManager.persist(user); return user; } public User insertWithoutTransaction(User user) { entityManager.persist(user); return user; } public User find(long id) { return entityManager.find(User.class, id); } }

次のクラスExtendedPersistenceContextUserServiceは、@ PersistenceContextアノテーションを除いて、上記と非常によく似ています。今回は、PersistenceContextType.EXTENDED@PersistenceContextアノテーションのtypeパラメーターに渡します。

@Component public class ExtendedPersistenceContextUserService { @PersistenceContext(type = PersistenceContextType.EXTENDED) private EntityManager entityManager; // Remaining code same as above }

5.テストケース

サービスクラスを設定したので、トランザクション永続コンテキストと拡張永続コンテキストを使用してさまざまなユースケースを作成します。

5.1トランザクションの永続性コンテキストのテスト

トランザクションスコープの永続コンテキストを使用して、ユーザーエンティティを永続化しましょう。エンティティは永続ストレージに保存されます。次に、拡張永続コンテキストのEntityManagerを使用してfind呼び出しを行うことで検証します。

User user = new User(121L, "Devender", "admin"); transctionPersistenceContext.insertWithTransaction(user); User userFromTransctionPersistenceContext = transctionPersistenceContext .find(user.getId()); assertNotNull(userFromTransctionPersistenceContext); User userFromExtendedPersistenceContext = extendedPersistenceContext .find(user.getId()); assertNotNull(userFromExtendedPersistenceContext);

トランザクションなしでUserエンティティを挿入しようとすると、TransactionRequiredExceptionがスローされます。

@Test(expected = TransactionRequiredException.class) public void testThatUserSaveWithoutTransactionThrowException() { User user = new User(122L, "Devender", "admin"); transctionPersistenceContext.insertWithoutTransaction(user); }

5.2拡張永続コンテキストのテスト

次に、拡張された永続コンテキストを使用して、トランザクションなしでユーザーを永続化しましょう。ユーザエンティティは永続コンテキスト(キャッシュ)ではなく、永続ストレージに保存されます。

User user = new User(123L, "Devender", "admin"); extendedPersistenceContext.insertWithoutTransaction(user); User userFromExtendedPersistenceContext = extendedPersistenceContext .find(user.getId()); assertNotNull(userFromExtendedPersistenceContext); User userFromTransctionPersistenceContext = transctionPersistenceContext .find(user.getId()); assertNull(userFromTransctionPersistenceContext);

永続エンティティIDの永続コンテキストには、一意のエンティティインスタンスがあります。同じ識別子を持つ別のエンティティを永続化しようとすると、次のようになります。

@Test(expected = EntityExistsException.class) public void testThatPersistUserWithSameIdentifierThrowException() { User user1 = new User(126L, "Devender", "admin"); User user2 = new User(126L, "Devender", "admin"); extendedPersistenceContext.insertWithoutTransaction(user1); extendedPersistenceContext.insertWithoutTransaction(user2); }

EntityExistsExceptionが表示されます

javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session

トランザクション内の拡張永続コンテキストは、トランザクションの終了時にエンティティを永続ストレージに保存します。

User user = new User(127L, "Devender", "admin"); extendedPersistenceContext.insertWithTransaction(user); User userFromDB = transctionPersistenceContext.find(user.getId()); assertNotNull(userFromDB);

拡張永続コンテキストは、トランザクション内で使用されると、キャッシュされたエンティティを永続ストレージにフラッシュします。まず、トランザクションなしでエンティティを永続化します。次に、トランザクションで別のエンティティを永続化します。

User user1 = new User(124L, "Devender", "admin"); extendedPersistenceContext.insertWithoutTransaction(user1); User user2 = new User(125L, "Devender", "admin"); extendedPersistenceContext.insertWithTransaction(user2); User user1FromTransctionPersistenceContext = transctionPersistenceContext .find(user1.getId()); assertNotNull(user1FromTransctionPersistenceContext); User user2FromTransctionPersistenceContext = transctionPersistenceContext .find(user2.getId()); assertNotNull(user2FromTransctionPersistenceContext);

6.結論

このチュートリアルでは、永続性のコンテキストについて十分に理解しました。

最初に、トランザクションの存続期間を通じて存在するトランザクションの永続性コンテキストを調べました。次に、複数のトランザクションにまたがることができる拡張永続コンテキストを確認しました。

いつものように、サンプルコードはGitHubで入手できます。