[JPA/김영한] 프록시와 지연로딩의 필요성

수영·2021년 10월 7일
2

JPA 공부!

목록 보기
7/9

이 글은 김영한님의 JPA 강의 중 8장 프록시와 연관관계 관리를 듣고 정리한 내용입니다 :)
강의 : 자바 ORM 표준 JPA 프로그래밍 - 기본편
교재 : 자바 ORM 표준 JPA 프로그래밍🤷‍♀️

엔티티 조회는 2가지 방식으로 할 수 있는데,

  1. em.find() : 실제 엔티티 객체 조회하기
  2. em.getReference() : DB 조회를 미루는, 가짜 엔티티 객체를 조회 (DB에 쿼리가 안나감)
    실제 사용될 시점에 DB조회가 이뤄짐

이때, 2번 getReference 방법을 사용하면 가짜 엔티티 객체를 조회하게 되는데,
이때 이 가짜 엔티티를 프록시라 한다! 이 프록시에 대해서 알아보자-!


❔ 프록시란

  • 프록시는 하이버네이트가 만든 가짜 클래스로 실제 객체의 참조를 보관한다.
  • target 속성이
    처음에는 null로 설정되어있다가, (처음에는 DB 조회 안 하니까)
    실제 사용할 때 실제 DB 조회가 일어나고, 이때 실제 엔티티를 참조할 수 있게 바뀐다.

프록시 객체의 초기화 과정은 아래와 같다!

프록시의 특징

  1. 프록시는 처음 사용할 때 한 번만 초기화된다.
  2. 프록시 초기화할때, target 속성으로 실제 엔티티를 참조하는 것
  3. 프록시 객체가 실제 엔티티 객체로 바뀌는 것이 아니라, 원본 엔티티를 참조받는 것이다.

여기서 주의! 타입 비교할때, ==으로 비교하면 안 되고 instance of를 사용한다.
참고로 영속성 컨텍스트에 있으면 == 으로 했을 때 같은 값, 즉 동일성을 보장함 ⇒ JPA의 특징

  1. 준영속 상태일 때 프록시 초기화하면 문제 발생
    ⇒ org.hibernate.LazyInitializationException

em.detached(); 혹은 em.closed(); 으로 준영속 상태를 만들수 있음
em.clear()하면 영속성 컨텍스트를 꺼버림

둘다 프록시 객체를 초기화 못하고 에러 발생!

⇒ 아예 어노테이션으로 준영속상태 안되게끔 ⇒ 추가

프록시를 직접 확인하려면?

  • 초기화했는지 확인하기 : PersistenceUnitUtil.isLoaded(Object entity)
Member refMember = em.getReference(Member.class, member1.getId()); //Proxy
// 프록시가 실제 사용된적이 없기에 초기화 안되었음
em.getPersistenceUnitUtil().isLoaded(); // false
  • 프록시의 클래스 확인하기 : refMember.getClass()
Member refMember = em.getReference(Member.class, member1.getId()); //Proxy
// 프록시가 실제 사용된적이 없기에 초기화 안되었음
refMember.getClass(); 
  • 프록시 강제 초기화시키기 : org.hibernate.Hibernate.initialize(entity명);
Member refMember = em.getReference(Member.class, member1.getId()); //Proxy
Hibernate.initialize(refMember); //강제 초기화

참고로 이 강제초기화 방법은 하이버네이트의 방식이고, JPA 표준은 강제 초기화 방법이 따로 없다.
하이버네이트 말고는, 강제 호출은 member.getName()을 사용할 것.


실무에서는 지연 로딩을 쓰자!

지연 로딩 : 프록시 객체로 실제 엔티티 객체 참조

즉시 로딩 : 연관관계에 있는 객체까지 바로 조회

// 즉시 로딩 - 패치 타입 EAGER 설정
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "team_id")
private Team team;

// 지연 로딩 - 패치 타입 LAZY 설정
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;

즉시 로딩 대신에 지연로딩을 쓰는 이유?

  • 즉시 로딩을 쓰면, 모든 테이블 다 연관시키고, 생각 못한 쿼리가 나간다.
  • JPQL에서는 문제 발생한다. JPQL는 SQL로 해석하는 데, N+1 (추가쿼리)가 나간다.

=> 이론적으로는 자주 함께 사용하는 연관관계끼리는 즉시 로딩, 아니면 지연로딩을 쓰지만,
실무에서는 그냥 다 지연 로딩을 사용하자!
특히 @ManyToOne, @OneToOne은 기본이 즉시 로딩이니까 → LAZY로 바꾸어야 함!

profile
🎵🎵🎵🎶🎵

0개의 댓글