JPA와 Proxy
in Framework on Spring Data JPA
프록시에 대하여
프록시를 설명하기 전에 Member를 조회할 때 Team도 DB에서 함께 조회를 해야하는가에 대한 질문을 해볼 수 있다.
이는 비즈니스 상황에 따라 다를 수 있다.
어떤 경우라면 Member만 단순 조회를 해와도 되는 경우가 있고, 어떤 경우에는 Member와 Team을 항상 같이 조회를 해와야할 수도 있다.
JPA 입장에서는 한 번에 둘 다 가져오는 경우라면 괜찮지만, Member만 사용하는 경우에 Team까지 조회하는 것은 낭비가 될 수 있다.
프록시 기초
JPA에서는 em.find() 말고도 em.getReference()라는 메서드가 있다.
em.getReference()는 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체를 조회한다.
즉, DB에 쿼리가 안날아가는데 객체가 조회되는 것이다. (으응?…)
즉 이 em.getReference()가 호출되는 시점에는 DB에 쿼리가 발생하지않는다.
그러고 이 객체가 실제로 사용되는 시점에 쿼리가 날아간다.
그러면 em.getReference()이 값이 반환하는 객체는 무엇일까?
이 클래스는 하이버네이트가 강제로 만든 프록시 클래스이다.
이 프록시 클래스 내부에는 Target이라는 속성이 존재하는데 이 Target이 진짜 클래스의 Reference를 갖고 있다.
프록시 특징
실제 클래스를 상속 받아서 만들어진다.
실제 클래스와 겉 모양이 같다.
사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용해도 된다.
프록시 객체는 실제 객체의 참조를 보관한다.
그 후 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드를 호출한다.
프록시 객체의 초기화
Member member = em.getReference(Member.class, "id1");
member.getName();
JPA 표준 스펙에는 이 프록시에 대한 정의가 없지만 이러한 프록시 객체의 구현은 JPA 구현체마다 다르다.
프록시의 특징
프록시 객체는 처음 사용할 때 한 번만 초기화된다.
프록시 객체를 초기화할 때, 프록시 객체가 실제 엔티티로 바뀌는게 아니다.
초기화되면 프록시 객체를 통해 실제 엔티티에 접근이 가능한 것이다.
프록시 객체는 원본 엔티티를 상속받으므로, 타입 체킹시에 유의해야한다.
영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해도 실제 엔티티를 반환한다.
영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제가 발생한다. (JPA를 사용하면 반드시 만나게될 예외…)
Reference
인프런 김영한님의 자바 ORM 표준 JPA 프로그래밍 - 기본편