프록시는 원본 엔티티를 상속받아서 만들어지므로 엔티티를 사용하는 클라이언트는 엔티티가 프록시인지 원본 엔티티인지 구분하지 않고 사용할 수 있다.
따라서 원본 엔티티를 사용하다가 지연 로딩을 하려고 프록시로 변경해도 클라이언트의 비즈니스 로직을 수정하지 않아도 된다.
하지만 프록시를 사용하는 방식의 기술적인 한계로 인해 예상하지 못한 문제들이 발생하기도 한다.
영속성 컨텍스트는 자신이 관리하는 영속 엔티티의 동일성을 보장한다.
프록시로 조회한 엔티티의 동일성도 보장할까?
@Testpublic void persistContextAndProxy(){ Member newMember = new Member("member1", "회원1"); em.persist(newMember); em.flush(); em.clear(); Member refMember = em.getReference(Member.class, "member1"); Member findMember = em.find(Member.class, "member1"); System.out.println("refMember Type = " + refMember.getClass()); System.out.println("findMember Type = " + findMember.getClass()); Assert.assertTrue(refMember == findMember); //성공}
출력 결과
refMember Type = class jpabook.advanced.Member_$$_jvst843_0
findMember Type = class jpabook.advanced.Member_$$_jvst843_0
먼저 member1을 em.getReference()를 사용해 프록시로 조회했다.
다음으로 같은 member1을 em.find()를 사용해 조회했다.
refMember는 프록시고 findMember는 원본 엔티티이므로 서로 다른 인스턴스로 생각할 수 있지만 이렇게 되면 영속성 컨텍스트가 영속 엔티티의 동일성을 보장하지 못하는 문제가 발생한다.
그래서 영속성 컨텍스트는 프록시로 조회된 엔티티에 대해서 같은 엔티티를 찾는 요청이 오면 원본 엔티티가 아닌 처음 조회된 프록시를 반환한다.
코드에서 member1 엔티티를 프록시로 처음 조회했기 때문에 이후에 em.find()를 사용해서 같은 member1을 찾아도 영속성 컨텍스트는 원본이 아닌 프록시를 반환한다.
출력 결과를 보면 $$_jvst843_0이 붙어있으므로 프록시로 조회된 것을 확인할 수 있다. 그리고 마지막에 assertTrue 검증 코드를 통해 둘이 같은 인스턴스인 것을 알 수 있다.