[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.