1 분 소요

JPA Proxy

Proxy 는 다른 객체를 대리하거나 감싸는 객체로, 그 객체에 대한 접근을 제어하거나 특정 작업을 수행하기 위해 사용한다.

JPA에서는 LAZY Loading을 구현하는 데 활용하게 되는데 실제 엔티티 클래스를 상속받아 만들어지므로 실제 엔티티의 동작을 수행한다.

Proxy 객체의 초기화는 아래와같이 EntityManager의 메서드인 getReference() 를 사용하면 된다.

Member member = em.getReference(Member.class, "id");
member.getName();

Client가 getReference()를 호출했을 때, 1차 캐시에 존재한다면 프록시 객체가 아닌 실제 객체를 반환하지만, 1차 캐시에 존재하지 않는다면 프록시 객체는 영속성 컨텍스트에 getName() 초기화 요청을 한다. 이때 프록시 객체를 생성하고 영속성 컨텍스트에 등록 하게 되는데 프록시 객체가 실제 엔티티의 동작을 수행하지 않고, 실제 데이터가 필요한 시점까지 데이터베이스 조회를 미루게 된다.

그리고나서 코드에서 실제 엔티티의 필드나 메서드에 접근하려고 할 때, 프록시는 영속성 컨텍스트에 초기화를 요청하고 이때 실제 데이터베이스 조회가 일어난다. 조회 후 실제 Entity 생성되어 프록시 객체가 해당 엔터티를 참조하게 하면 이때 이 참조를 통해 getName을 호출하게하는 것이다.

Proxy가 실제 Entity를 참조한다 ?

“참조”는 프록시 객체가 실제 엔티티 객체를 가리킨다는 의미로서 프록시 객체는 실제 엔티티 객체를 감싸고 있는 Wrapper이며 이 래퍼가 내부적으로 실제 엔티티 객체를 참조하는 것.

즉, 필요한 시점에서 이 실제 엔티티 객체의 동작을 프록시가 수행할 수 있도록 한다는 것이다.

LAZY and EAGER

LAZY와 EAGER는 객체를 조회할 때의 옵션 중 하나이다.

Team team = new Team();
em.persist(team);
 
Member member = new Member();
member.setTeam(team);
em.persist(member);

// 이때 출력하면 Team의 필드값은 초기화되지 않아 정보가 없음.
System.out.println(member);

// 여기까지는 아직 프록시가 생성되지 않았음.
Member findMember = em.find(Member.class, member.getId());

// 실제로 team에 접근할 때 프록시가 생성됨.
Team teamProxy = findMember.getTeam();

LAZY를 사용할 경우 위와 같은 로직에서 findMember를 조회해올 때 Team에 대한 로직은 수행하지 않는다.

Team객체를 실제 사용하는 findMember.getTeam();을 호출할 때 영속성 컨텍스트에 있다면 이미 로딩된 데이터를 반환,

영속성 컨텍스트에 없다면 Team에 대한 쿼리가 실행됨. 이를 ‘지연로딩(LAZY)’라 한다.

EAGER를 사용하는 경우에는 Member findMember = em.find(member.getId()); 하는 순간 Team객체까지 모두 조회할 것.

결론적으로, 실제 사용하는 member.getTeam();을 호출할 때 프록시 객체가 생성이 되며,

‘Team’ 엔티티에 대한 데이터베이스 조회도 이 때 실행된다.

업데이트: