
프록시(Proxy)란, 영문 그대로 '대리자'라는 뜻으로, 실제 객체를 대신하여 대리 역할을 수행하는 객체를 말한다.
위 사진과 같이 프록시는 실제 클래스를 상속받아서 만들어지기떄문에 실제 클래스 똑같이 생겼다.
이론상 사용자 관점에서는 진짜 객체인지 프록시인지 구분하지 않고 사용할 수 있다.
프록시 객체는 실제 객체의 참조(target)을 가지고 있으므로 프록시 객체를 호출하면 실제 객체의 메소드를 호출한다.
em.getReference()를 하여 프록시 객체를 생성할 수 있다.
다음 그림은 em.getReference()을 통해 MemberProxy를 생성후 .getName()을 호출했을떄 동작 과정을 나타낸 그림이다.(영속성 컨텍스트가 클린(clean)한 상태 가정)

프록시 객체는 처음 사용할 때 한 번만 초기화된다.
프록시를 초기화한다는 것은 프록시 객체가 실제 엔티티로 바뀌는것이 아니다.
Member findMember = em.getReference(Member.class, member.getId());
System.out.println("findMember.getClass() = " + findMember.getClass());
findMember.getUsername();
System.out.println("findMember.getClass() = " + findMember.getClass());
실행 결과:
findMember.getClass() = class hellojpa.Member$HibernateProxy$NXbKWHsJ
Hibernate:
select
m1_0.MEMBER_ID,
m1_0.createBy,
m1_0.createdDate,
m1_0.lastModifiedBy,
m1_0.lastModifiedDate,
t1_0.TEAM_ID,
t1_0.name,
m1_0.USERNAME
from
Member m1_0
left join
Team t1_0
on t1_0.TEAM_ID=m1_0.team_TEAM_ID
where
m1_0.MEMBER_ID=?
findMember.getClass() = class hellojpa.Member$HibernateProxy$NXbKWHsJ
프록시 객체는 원본 엔티티를 상속 받으므로
타입체크 를 주의해야 한다. 타입 체크시 == 비교를 사용하지 않고 instance of를 사용하자.
Member RefMember = em.getReference(Member.class, member.getId());
System.out.println("RefMember.getClass() = " + RefMember.getClass());
System.out.println(" RefMember instanceof Member = " + (RefMember instanceof Member));
실행결과:
RefMember.getClass() = class hellojpa.Member$HibernateProxy$uyIJHorZ
RefMember instanceof Member = true
영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해 도 실제 엔티티 반환된다.
Member findMember = em.find(Member.class, member.getId());
System.out.println("findMember.getClass() = " + findMember.getClass());
findMember.getUsername();
Member RefMember = em.getReference(Member.class, member.getId());
System.out.println("RefMember.getClass() = " + RefMember.getClass());
실행 결과:
findMember.getClass() = class hellojpa.Member
findMember.getClass() = class hellojpa.Member
System.out.println("================");
Member findMember = em.find(Member.class, member.getId());
System.out.println("findMember.getClass() = " + findMember.getClass());
Member RefMember = em.getReference(Member.class, member.getId());
System.out.println("RefMember.getClass() = " + RefMember.getClass());
em.clear();
System.out.println("================");
Member RefMember2 = em.getReference(Member.class, member.getId());
System.out.println("RefMember2.getClass() = " + RefMember2.getClass());
Member findMember2 = em.find(Member.class, member.getId());
System.out.println("findMember2.getClass() = " + findMember2.getClass());
System.out.println("findMember == RefMember:" + (findMember==RefMember));
System.out.println("findMember2 == RefMember2:" + (findMember2==RefMember2));
실행 결과:
================
Hibernate:
select
m1_0.MEMBER_ID,
m1_0.createBy,
m1_0.createdDate,
m1_0.lastModifiedBy,
m1_0.lastModifiedDate,
t1_0.TEAM_ID,
t1_0.name,
m1_0.USERNAME
from
Member m1_0
left join
Team t1_0
on t1_0.TEAM_ID=m1_0.team_TEAM_ID
where
m1_0.MEMBER_ID=?
findMember.getClass() = class hellojpa.Member
RefMember.getClass() = class hellojpa.Member
================
RefMember2.getClass() = class hellojpa.Member$HibernateProxy$dfy1PGsC
Hibernate:
select
m1_0.MEMBER_ID,
m1_0.createBy,
m1_0.createdDate,
m1_0.lastModifiedBy,
m1_0.lastModifiedDate,
t1_0.TEAM_ID,
t1_0.name,
m1_0.USERNAME
from
Member m1_0
left join
Team t1_0
on t1_0.TEAM_ID=m1_0.team_TEAM_ID
where
m1_0.MEMBER_ID=?
findMember2.getClass() = class hellojpa.Member$HibernateProxy$dfy1PGsC
================
findMember == RefMember:true
findMember2 == RefMember2:true
Member refMember = em.getReference(Member.class, member.getId());
em.detach(refMember);
System.out.println("refMember.getUsername() = " + refMember.getUsername());
org.hibernate.LazyInitializationException: could not initialize proxy [hellojpa.Member#1]
//이하 생략...
참고 자료 출처: 출처