Proxy를 왜 사용하게 됐을까?
위 질문에 답하기 전에 만약 Member를 조회할 때 Team도 함께 조회를 해야 할까? 하는 질문에 대한 대답을 생각해보자

private static void searchMember(Member member) {
member.getUsername();
}
멤버의 이름을 조회하는 코드를 실행할 때 만약 Team까지 같이 조회한다면 낭비가 발생한다. 다시 말해 최적화가 안되었다는 뜻이다. 이런 문제를 해결하기 위해 사용하는 것이 프록시이다.
getReference()를 사용하는 시점에는 DB에 쿼리를 날리지 않음
데이터가 실제 사용되는 시점에 DB에 쿼리를 날림

Proxy객체에는 Entity target이란 것이 있다. Entity target은 실제 엔티티의 위치를 나타낸다. 초기에는 null값이 들어있다.

그리고 초기화를 하면 프록시 객체는 영속성 컨텍스트에 초기화 요청을 하고 영속성 컨텍스트는 DB를 조회한다 그럼 실제 Entity가 생성되고 그 Entity의 위치가 프록시 객체 target에 저장된다.
Member findMember = em.getReference(Member.class, member.getID());
System.out.println("findMember = " + findMember.getClass());
System.out.println("findMember.id = " + findMember.getId());
System.out.println("findMember.username = " + findMember.getUsername());

class를 조회했을 때 getReference()를 이용해 조회하면 클래스 이름 뒤에 Proxy주소가 나온다. 즉, 하이버네이트가 강제로 만든 가짜 클래스라는 얘기다.
// 실제 엔티티가 영속성 컨텍스트에 올라감
Member m1 = em.find(Member.class, member1.getId());
System.out.println("m1 = " + m1.getClass());
// getReference()를 이용해 객체를 조회해도 이미 엔티티가 영속성 컨텍스트에 올라가 있기 때문에 실제 엔티티가 조회됨
Member reference = em.getReference(Member.class, member1.getId());
System.out.println("reference = " + reference.getClass());
// 같은 트랜잭션 내에 있는 엔티티는 같은 엔티티를 반환해야 함
// 따라서 em.find()로 객체를 조회 했어도 프록시를 반환할 수가 있음
private static void logic(Meber m1, Member m2) {
// true return
Member m1 = em.find(Member.class, member1.getId());
Member m2 = em.find(Member.class, member2.getId());
System.out.println("m1 == m2 : " + (m1.getClass() == m2.getClass()));
// false return
Member m1 = em.find(Member.class, member1.getId());
Member m2 = em.getReference(Member.class, member2.getId());
}
// 실제로는 이렇게 메서드를 이용하는 경우가 많음. 매개변수로 실제 엔티티가 넘어올지 프록시가 넘어올지 알 수 없음
private static void logic(Member m1, Member m2){
System.out.println("m1 == m2: " + (m1.getClass() == m2.getClass()));
// 따라서 == 비교 대신 instanceof를 사용해야함
System.out.println("m1 == m2: " + (m1 instanceof Member));
System.out.println("m1 == m2: " + (m2 instanceof Member));
}
Proxy instance의 초기화 여부 확인
PersistenceUnitUtil.isLoaded(Object entity)
Proxy Class 확인 방법
entity.getClass().getName() 출력
Proxy 강제 초기화
org.hibernate.Hibernate.initialize(entity);
참고 : JPA는 강제 초기화 없음
강제 호출 : member.getName()