JPA持続性コンテキスト


JPA持続性コンテキスト


1.持続性コンテキストの特徴


永続性コンテキストは、識別子値(@Idがテーブルにマッピングされたプライマリ・キー)で区切られます.
したがって、永続状態には識別子値が必要です.識別子値がない場合、例外が発生します.
エンティティが永続性コンテキストに格納されている場合は、すぐにデータベースに格納されません.トランザクションがコミットされる前に、永続性コンテキストでコミットが実行されると、플러시と呼ばれるメモリ領域(書き込み遅延SQLストレージと呼ばれる)に格納されているSQL文をデータベースに同期するためにリフレッシュが呼び出されます.次に、リフレッシュについてより詳細に説明します.

2.持続性コンテキストの利点

  • メインキャッシュ
  • 同一性保障
  • トランザクションをサポートする書き込み遅延
  • 変更検出
  • 遅延ロード
  • 上記の永続性コンテキストの利点をレビューし、CRUDエンティティの利点を一例で説明します.

    3.エンティティCRUDサンプルコード


    エンティティクエリー

    Member member = new Member();
    member.setId("member1");
    member.setUsername("회원");
    
    em.save(member);
    このコードを実行すると、メンバー・エンティティは永続コンテキストに存在するプライマリ・キャッシュに格納されます.
    メンバーエンティティはデータベースに保存されていません.プライマリ・キャッシュは、Keyとvalueの値を持つMap形式で収集されたデータ構造です.
    Keyは、メンバーインスタンス(エンティティ値)を含む識別子値@Idです.識別子値は、データベースのプライマリ・キーにマッピングされます.
    したがって、永続性コンテキストでデータを格納およびクエリーするすべての条件は、データベースのデフォルトのキー値です.

    エンティティクエリーは次のコードで示されます。

    Member member = em.find(Member.class, "member1");
    find()メソッドでは、最初のパラメータはエンティティクラスタイプで、2番目のパラメータはクエリーするエンティティの識別子値です.このメソッドを呼び出すと、まずプライマリ・キャッシュでエンティティが検索され、存在しない場合はデータベースでクエリーしてエンティティが作成されます.次に、プライマリ・キャッシュに保存し、永続的なエンティティに戻ります.

    このようにしてデータベースで1回クエリーし、永続的なコンテキストに保存すると、メモリのプライマリ・キャッシュからすぐにロードされ、パフォーマンスのメリットが得られます.

    エンティティの登録


    エンティティマネージャを使用して永続性コンテキストにエンティティを登録する手順を見てみましょう.
    EntityManager em = emf.createEntityManager();
    EntityTransaction transaction = em.getTransaction();
    //엔터티 매니저는 데이터 변경 시 트랜잭션을 시작합니다.
    transaction.begin(); // 트랜잭션 시작
    
    em.save(memberA);
    em.save(memberB);
    //여기까지 Insert SQL을 데이터베이스에 보내지 않습니다.
    
    transaction.commit(); // 트랜잭션 커밋
    トランザクションをコミットする前に、エンティティーマネージャは、エンティティをデータベースに格納するのではなく、書き込み遅延SQLリポジトリに内部クエリーを順番に積み上げ、実際にトランザクションが停止したときに収集されたクエリーをデータベースに送信します.これは、トランザクションをサポートする書き込み遅延と呼ばれます.
    トランザクションをコミットすると、エンティティ管理者はまず永続性コンテキストをリフレッシュします.
    リフレッシュは、永続性コンテキストの変更をデータベースに同期させる操作です.登録、変更、削除されたエンティティをデータベースに反映します.つまり、遅延書き込みSQLリポジトリに集約されたクエリーをデータベースに送信できます.これらの永続性コンテキストの変更をデータベースに同期すると、実際のデータベーストランザクションがコミットされます.
    どうせ.データを保存した直後に登録クエリーをデータベースに送信するか、データを保存する場合は、データベースに送信せずに登録クエリーをメモリに保存してトランザクションをコミットすると、収集したクエリーをデータベースに送信するすべての方法が複数のトランザクション範囲内で実行されるため、両方の結果は同じです.結果は...トランザクションをコミットする前に、これは役に立ちません.いずれにしても、SQL文をコミットする前にデータベースに渡すだけです.これは、書き込み遅延がトランザクションをサポートする理由です.
    これらの機能を使用すると、収集した登録クエリーを一度にデータベースに送信してパフォーマンスを最適化できます.

    エンティティの変更


    SQLでは、自分で変更クエリーを作成する必要があります.プロジェクトの拡大と需要の増加に伴い、クエリーの変更も増加しています.次のクエリを変更します.
    update MEMBER
    SET
        NAME=?
        AGE=?
    WHERE
        id=?
    メンバー名と年齢を変更する機能が開発されました.メンバー・レベルを変更する機能が追加された場合は、次のように変更クエリーを作成する必要があります.
    UPDATE MEMBER
    SET
       GRADE=?
    WHERE
       id=?
    通常、2つの変更クエリーが作成されます.もちろん、次のように変更クエリーを使用するだけで、両方を組み合わせて使用できます.
    UPDATE MEMBER
    SET 
       NAME=?
       AGE=?
       GRADE=?
    WHERE
       id=?
    ただし、これらの組合せクエリーを使用して名前と年齢を変更したり、レベルを変更するときに名前と年齢を入力したりすることはできません.
    この開発方法では、クエリを大量に変更するだけでなく、SQLをチェックし続け、ビジネスロジックを分析する必要があるという問題があります.最終的には、直接的にも間接的にも、ビジネスロジックはSQLに依存します.
    これらの問題を解決するために、JPAは変更検出機能を提供しています.
    JPAを使用してエンティティを変更する場合は、エンティティを参照してデータを変更するだけです.
    エンティティのデータのみが変更され、データベースに反映される方法...エンティティの変更をデータベースに自動的に反映する機能を変更検出と呼びます.
    エンティティを永続性コンテキストに保存すると、JPAはスナップショットと呼ばれる初期状態をコピーして保存します.次に、スナップショットとエンティティをリフレッシュポイントで比較して、変更したエンティティを検索します.

    上図が一番よく説明されているようなので添付しました.変更検出は、エンティティが永続コンテキストに存在しない場合、変更データを持つエンティティはデータベースに保存されません.永続的なエンティティのみが適用されます.

    エンティティの削除


    エンティティを削除するには、削除するエンティティをクエリーする必要があります.
    Member memberA = em.find(Member.class, "memberA); //삭제 대상 엔터티 조회
    em.remove(memberA);
    
    em.remove();
    削除するエンティティをem.remove()に移行すると、エンティティが削除されます.もちろん、削除クエリは、直ちにエンティティを削除するのではなく、書き込みが遅延しているSQLリポジトリに登録されます.
    トランザクションをコミットしてリフレッシュを呼び出すと、削除クエリーが実際のデータベースに渡されます.
    em.remove(memberA)が呼び出されると、memberAは永続性コンテキストから削除されることに注意してください.
    削除したエンティティを繰り返し使用するのではなく、自然にゴミ収集のターゲットにすることをお勧めします.

    4.ブラシとは?


    リフレッシュ(flush()は、永続性コンテキストの変更をデータベースに反映します.リフレッシュでは、次の状況が発生します.

  • 変更が検出されると、永続コンテキスト内のすべてのエンティティがスナップショットと比較され、変更されたエンティティが検索されます.変更後のエンティティは、変更クエリーを作成し、書き込みが遅延しているSQLリポジトリに登録します.

  • 書き込み遅延SQLリポジトリに対するクエリーをデータベースに送信します.(登録、変更、削除)
  • 永続性コンテキストをリフレッシュする方法は3つあります。

  • em.flush()を直接呼び出します.
  • トランザクションがコミットされると、リフレッシュが自動的に呼び出されます.
  • JPQLクエリを実行すると、リフレッシュが自動的に呼び出されます.
  • ダイレクトコール


    エンティティマネージャのflush()メソッドを直接呼び出し、永続性コンテキストを強制的にリフレッシュします.
    テストやその他のフレームワークをJPAと一緒に使用する以外は、あまり使用されません.

    トランザクションのコミット時にリフレッシュを自動的に呼び出す


    変更をデータベースに渡さずにトランザクションのみをコミットする場合、データベースにはデータは反映されません.したがって、トランザクションをコミットする前に、永続性コンテキストへの変更を反映するためにリフレッシュを呼び出す必要があります.
    JPAでは、トランザクションの発行時にリフレッシュが自動的に呼び出され、それを回避します.

    JPQLクエリの実行時に自動的にリフレッシュを呼び出す


    リフレッシュは、JPQLやCriteriaなどのオブジェクトへのクエリーを呼び出すときにも実行されます.
    例を見てみましょう.
    //save 메소드는 JPA 구현체인 하이버네이트에서 제공해주는 함수로 persist처럼 해당 엔터티를 영속화 시킵니다.
    em.save(memberA);
    em.save(memberB);
    em.save(memberC);
    
    //중간에 JQPL 실행
    query = em. createQuery("select m from Member m", Member.class);
    List<Member> members = query.getResultList();
    まず、em.save()またはem.persist()を呼び出すことで、エンティティメンバーA、メンバーB、およびメンバーCを永続的にします.これらのエンティティは永続性コンテキストにありますが、データベースには反映されていません.JPQLを実行すると、JQGLはSQLに変換され、データベースでエンティティが照会されます.
    ただし、memberA、memberB、memberCはデータベースに存在しないため、クエリー結果としてクエリーは行われません.したがって、クエリーを実行する前に、永続コンテキストをリフレッシュして、変更をデータベースに反映する必要があります.
    同様に、JPAは、このような事態を回避するために、JPQLの実行時にリフレッシュを自動的に呼び出す.

    参考文献:Java ORM標準JPAプログラミング