🍍

JPAのProxyとは

2022/06/06に公開

JPAのProxy

Proxy(プロキシー)

  • Proxyの辞書的意味合いとしては「代理」という意味を持っています。
  • JPAのProxyは実際のクラスを継承して作られた偽物を意味します。
  • 外観は実際のクラスとそっくりです。
  • 使用する立場としては本物なのか偽物なのか関係なくただ使えればいいのです。(理論上)

  • Proxyは本物の参照(reference)を持っています。
  • Proxyのメソッドを呼び出すとProxyは本物のメソッドを呼び出します。

Proxyの初期化

  • Proxyは最初使用する時一回のみ初期化されます。
  • Proxyを初期化する時Proxyが本物に変わることではないです。
  • 初期化されるとProxyを通じて本物に接近可能になります。
Member member = em.getReference(Member.class, 1L);
member.getName(); // この時点に初期化を要請

  1. クライアントが本物のEntityの値が知りたい、getName()を呼び出し。
  2. ProxyはPersistenceContextに初期化要請
  3. PersistenceContextに該当のオブジェクトがない場合DBを照会
  4. 本物のEntityを生成
  5. target(参照)を通じて本物のgetName()を呼び出し値を返す。

ProxyとEntityの比較

  • Proxyは本物のEntityを継承する、よってタイプをチェックする時に注意が必要です。
    • ==比較はfalseです、instance ofを使用しましょう
    • ==でやるタイプ比較は継承しても違うと判断します。
Member member1 = new Member();
member1.setName("member1");
em.persist(member1);

Member member2 = new Member();
member2.setName("member2");
em.persist(member2);

em.flush();
em.clear();

/* 本物同士の比較 */
Member m1 = em.find(Member.class, member1.getId());
Member m2 = em.find(Member.class, member2.getId());
// 当然ながらtrue
System.out.println("m1 == m2 : " + (m1.getClass() == m2.getClass());

/* 本物MemberオブジェクトとProxy比較 */
Member m1 = manager.getReference(Member.class, member1.getId());
Member m2 = manager.find(Member.class, member2.getId());
// False ! 一つはMemberオブジェクトもう一つはMember継承して作ったProxy
System.out.println("m1 == m2 : " + (m1.getClass() == m2.getClass()));
// True ! Proxyのタイプ比較はinstanceofを使用しなければなりません
System.out.println("m1 == Member : " + (m1 instanceof Member))
  • PersistenceContextに探しているEntityが既に存在するとem.getReference()を呼び出しても本物を返します。
    • JPAは同じPersistenceContextの中ではある一つのインスタンスに対してはいつも同じである事を保証しなければならないです。
  • PersistenceContextの助けを求められない時に(detachedされた状態)Proxyを初期化すると例外が発生します。
    • org.hibernate.LazyInitializationException
em.detach(refMember);
em.close();
em.clear();
// org.hibernate.LazyInitializationException  !
refMember.getName();

Proxyの照会

  • em.getReference(): データベース照会を後にするProxy照会メソッド
    • つまりDBにクエリを投げずにオブジェクト(Proxy)を照会する
    • 本物のオブジェクトの値に触れようとすると実際のクエリを投げる。
  • em.find() : データベースを通じて本物のエンティティを照会

Proxyの初期化状態確認

  • Proxyインスタンスの初期化状態確認
    • emf.getPersistenceUnitUtil().isLoaded(Entity)
    • emf -> EntityManagerFactory
  • Proxyクラス確認方法
    • entity.getClass().getName()
  • Proxy強制初期化
    • org.hibernate.Hibernate.initialize(entity);
    • hibernateが支援する機能でJPA標準ではない。
  • JPA標準にはないので本物の値を呼び出して強制的に初期化する。: member.getName()

(次)JPAのFetchType

https://zenn.dev/dev_yoon/articles/985691468ffc76

Discussion