Post

[JPA] 프록시(Proxy)

Proxy

프록시 특징


  • 프록시 객체는 처음 사용할 때 한번만 초기화
1
2
3
4
5
6
7
8
9
10
11
Member findMember = em.getReference(Member.class, member.getId());

//프록시 객체임. 모든 엔티티는 프록시 상속받고 있음.
System.out.println("findMember= "+findMember.getClass());
System.out.println("findMember.id= "+ findMember.getId());

//이때 쿼리날림 왜냐? 필요하니까. 그리고 가져온 실제 entity 객체를 프록시 객체가 가져다가 씀
System.out.println("findMember.username= "+ findMember.getUsername());

//쿼리를 날려서 실제 엔티티와 연결되었어도 findMember는 여전히 프록시 객체
System.out.println("findMember= "+findMember.getClass());
  • 프록시 객체가 실제 엔티티로 바뀌는 것이 아니고 초기화 되면 실제 엔티티에 접근이 가능한 것
  • 프록시가 초기화 되면 실제 엔티티랑 링크가 걸릴 것 뿐. 한번 getReference()로 가져온 프록시 객체는 계속 프록시 객체임!
  • 따라서 타입 비교는 ==가 아닌 instance of 를 사용하자!
1
2
3
4
5
6
7
8
Member findMember = em.find(Member.class, member.getId());
Member findMember2 = em.find(Member.class, member2.getId());

System.out.println("m1 == m2: " +  (findMember.getClass() == findMember2.getClass()));

---

m1 == m2: true
1
2
3
4
5
6
7
8
Member findMember = em.find(Member.class, member.getId());
Member findMember2 = em.getReference(Member.class, member2.getId());

System.out.println("m1 == m2: " +  (findMember.getClass() == findMember2.getClass()));

---

m1 == m2: false
  • 간단하게 출력했지만 이게 Member 타입을 인자로 받는 메서드로 들어가면 왜 false가 나왔는지 못 찾을 수도 있으니 객체 타입 비교는 instance of 로 합시다~
1
2
3
4
5
6
7
8
9
10
Member findMember = em.find(Member.class, member.getId());
Member findMember2 = em.getReference(Member.class, member2.getId());

System.out.println("m1 == Member: " +  (findMember instanceof  Member));
System.out.println("m2 == Member: " +  (findMember2 instanceof  Member));

---

m1 == Member: true
m2 == Member: true
  • 그러나 이미 영속성 컨텍스트에 찾는 객체가 있다면, getReference()로 해도 실제 엔티티가 반환된다. 왜냐면 이미 저장되어 있기 때문!
1
2
3
4
5
6
7
8
9
10
11
12
13
Member findMember = em.find(Member.class, member.getId());
System.out.println("m1 = "+ findMember.getClass());

Member findMember2 = em.getReference(Member.class, member.getId());
System.out.println("m2 = "+ findMember2.getClass());

System.out.println("m1 == m2 : "+ (findMember2.getClass() == findMember.getClass()));

---

m1 = class hellojpa.Member
m2 = class hellojpa.Member
m1 == m2 : true
  • 같은 프록시는 같은 프록시 객체!
1
2
3
4
5
6
7
8
9
10
11
12
13
Member findMember = em.getReference(Member.class, member.getId());
System.out.println("m1 = "+ findMember.getClass());

Member findMember2 = em.getReference(Member.class, member.getId());
System.out.println("m2 = "+ findMember2.getClass());

System.out.println("m1 == m2 : "+ (findMember2.getClass() == findMember.getClass()));

---

m1 = class hellojpa.Member$HibernateProxy$qSLSpcYp
m2 = class hellojpa.Member$HibernateProxy$qSLSpcYp
m1 == m2 : true
  • 프록시를 먼저 구하고 그다음에 find를 하면?
1
2
3
4
5
6
7
8
9
10
11
12
Member findMember = em.getReference(Member.class, member.getId());
System.out.println("m1 = "+ findMember.getClass());

Member findMember2 = em.find(Member.class, member.getId());
System.out.println("m2 = "+ findMember2.getClass());

System.out.println("m1 == m2 : "+ (findMember2.getClass() == findMember.getClass()));

---
m1 = class hellojpa.Member$HibernateProxy$Y26NjsWM
m2 = class hellojpa.Member$HibernateProxy$Y26NjsWM
m1 == m2 : true
  • JPA에서는 어떻게든 findMember와 findMember2를 맞추려고 하기 때문이라고 생각하는게 편하다..

  • 준영속 상태일 때 컨텍스트의 관리를 받지 못한다면 프록시 초기화하면 문제가 발생한다!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Member findMember = em.getReference(Member.class, member.getId());
System.out.println("m1 = "+ findMember.getClass());

em.detach(findMember);

String username = findMember.getUsername();
System.out.println("findMember.username = "+username);

tx.commit();

---

org.hibernate.LazyInitializationException: could not initialize proxy [hellojpa.Member#1] - no Session
	at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:170)
	at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:310)
	at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:45)
	at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95)
	at hellojpa.Member$HibernateProxy$nKPIrLxd.getUsername(Unknown Source)
	at hellojpa.JpaMain.main(JpaMain.java:43)

프록시 확인


  • 확인
1
2
3
4
5
6
7
8
9
System.out.println("isLoaded = "+ emf.getPersistenceUnitUtil().isLoaded(findMember));

findMember.getUsername();

System.out.println("isLoaded = "+ emf.getPersistenceUnitUtil().isLoaded(findMember));
---

isLoaded = false
isLoaded = true
  • 강제 초기화
1
2
3
4
Member findMember = em.getReference(Member.class, member.getId());
System.out.println("m1 = "+ findMember.getClass());

Hibernate.initialize(findMember); // 강제 초기화
This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.