[JPA 기본] 프록시 (Proxy)

강신현·2022년 7월 18일
0

✅ em.getReference()

필요성

다음 두가지 경우가 있다고 해보자

  1. 회원과 팀 함께 출력
public void printUserAndTeam(String memberId) {
	Member member = em.find(Member.class, memberId);
	Team team = member.getTeam();
	System.out.println("회원 이름: " + member.getUsername());
	System.out.println("소속팀: " + team.getName());
}
  1. 회원만 출력
public void printUser(String memberId) {
	Member member = em.find(Member.class, memberId);
	Team team = member.getTeam();
	System.out.println("회원 이름: " + member.getUsername());
}

회원과 팀을 항상 함께 출력한다면 문제가 되지 않지만
회원만 출력하는 경우에는 팀까지 불러올 필요가 없으므로 낭비가 생긴다.

이를 해결하기 위해 jpa에서 프록시지연로딩을 지원한다.

프록시 (Proxy)

- em.find() vs em.getReference()

em.find()

데이터베이스를 통해서 실제 엔티티 객체 조회

em.getReference()

데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체 조회

즉, em.getReference()가 선언되는 시점에는 db에 쿼리가 안찍히고
실제로 사용되는 시점에 db에 쿼리를 찍어 조회

- 특징

  • 실제 클래스를 상속 받아서 만들어진다.
    • 겉모양이 같다.
    • 타입 체크시 ==가 아닌, instance of를 사용한다.
Member m1 = em.find(Member.class, member1.getId());
Member m2 = em.find(Member.class, member2.getId());
Member m3 = em.getReference(Member.class, member3.getId());

System.out.println("m1 == m2 : " + (m1.getClass() == m2.getClass())); // true
System.out.println("m1 == m3 : " + (m1.getClass() == m3.getClass())); // false

System.out.println("m3 : " + (m3 instanceof Member)); // true
  • 사용할 때는 진짜 객체인지 프록시 객체인지 구분할 필요가 없음
  • 실제 객체의 참조(target)을 보관한다. (프록시 객체 호출하면 프록시 객체가 실제 객체를 호출하는 구조)

- 초기화

Member member = em.getReference(Member.class, “id1”); 
member.getName();

  • 초기화 요청 : 처음에 MemberProxy에는 target이 비어있기 때문에 JPA가 영속성 컨택스트에 target을 요청한다. (== 진짜 객체 가져와!)
  • 한번 초기화 요청을 하면 target에 값이 저장되기 때문에 이후 또 초기화 요청을 할 필요 없음
    • 즉, 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해도 실제 엔티티가 반환된다.

영속성 컨텍스트 문제

영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제가 발생한다.

Member member = em.getReference(Member.class, “id1”);
em.detach(member); // or em.close();
member.getName();

em.detach 나 em.close에 의해 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태가 되기 때문에 이후 프록시를 초기화 하면 문제(에러)가 발생한다.
(하이버네이트가 org.hibernate.LazyInitializationException 예외를 터트림)

- 확인

  1. 프록시 초기화 여부 확인
emf.getPersistenceUnitUtil.isLoaded(Object entity)
  1. 프록시 클래스 확인
entity.getClass()
  1. 프록시 강제 초기화
org.hibernate.Hibernate.initialize(entity);
profile
땅콩의 모험 (server)

0개의 댓글