[3] JPA 프로그래밍 (9) - 프록시 / 지연로딩(LAZY) & 즉시로딩(EAGER)

김정욱·2021년 3월 8일
0

[3] JPA 프로그래밍

목록 보기
9/15
post-thumbnail

프록시 ?

[ Intro ]

  • 사전적 의미대리(행위)나 대리권, 대리인을 의미
    --> 어떤 것을 대신 해주는 것
  • 객체 내부에 특정 값이 아닌, 객체를 참조하는 객체지향 설계에서 프록시의 필요성에 대해 알아보자
  • 객체를 조회할 때 마다 내부 참조 객체에 대한 모든 정보를 가져올 필요는 없다
    --> 프록시 객체를 가져와서 필요할 때 가져오는 방식을 구현

[ 프록시 기초 ]

  • em.find() vs em.getReference()
    • em.find() : DB를 통해 실제 Entity 객체 조회
    • em.getReference() : DB조회를 미루는 가짜(프록시) Entity 객체 조회
  • 프록시 객체
    : 실제 Entity를 가리키는 target 이라는 값만 들어있다
    (최초에 targetnull값 보유)
    이후 실제 객체에 접근하는 method(getter)를 호출하면 그 때 실제 DB에 접근해서 target실제 Entity가 연결된다
  • 프록시 객체 동작 원리와 프록시 객체 초기화
    • 동작 원리1) em.getReference()로 호출하여 프록시 객체가 반환
           (target에는 null이 들어가 있다)
      2) 실제 객체에 접근하는 member.getName()호출
      3) targetnull이기 때문에 영속성 컨텍스트에 요청
      4) 영속성 컨텍스트는 실제 DB에서 조회한 후 target실제 Entity를 연결
      5) 프록시 객체target을 통해 .getName()호출
    • 프록시 객체 초기화
      : target이 실제 Entity와 연결되는 과정을 의미하는 것
      (프록시 객체 초기화는 최초 1회만 수행된다)
  • 추가 확인 메서드's
    • 프록시 인스턴스 초기화 여부 확인
      : EntitiyManagerFactor를 통해 얻는다
        emf.getPersistenceUnitUtil().isLoaded(Object object)
    • 프록시 클래스 확인
      : entity.getClass().getName()출력
    • 프록시 강제 초기화
      : org.hibernate.Hibernate.initialize(entity);
      (강제 초기화 수행시 그때 select가 나가서 Entity 연결해줌!)

[ 프록시 특징 & 주의점 ]

  • 프록시 객체 초기화는 최초 사용시 처음에만 수행
  • 프록시 객체 초기화프록시 객체실제 Entity로 바뀌는 것이 아니다
    --> 프록시 객체target실제 Entity가 연결될 뿐!
  • 프록시 객체원본 Entity를 상속받기 때문에 타입체크(TypeCheck) 주의!
    --> 항상 class Type Check는 ==이 아니라 instance of사용!
  • 영속성 컨텍스트찾는 Entity가 이미 있으면, em.getReference()해도 실제 Entity가 반환된다
    --> JPA는 동일 객체임을 보장하기 위해 프록시를 먼저 조회하면 프록시 객체로 맞추고, 아니면 실제 Entity에 맞추는 내부로직을 가지고 있음
    (그래서 JPA는 하나의 트랜잭션에서 같은 객체의 조회는 항상 동일 객체를 보장한다!)
  • 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태에서 프록시 초기화하면 오류 발생!
    --> 결국 프록시영속성 컨텍스트를 통해 일어난다는 것이 핵심.
    (LazyInitializationException)

지연로딩(LAZY)

[ 개념 ]

  • 내부 참조 객체실제 사용하는 시점에 조회하는 로딩 방법
  • 내부 참조 객체의 사용을 잘 하지 않는 로직에 효율적(이론적)
    --> 실제 실무에서 모두 LAZY로 설계하고 필요시 한방로딩을 해주는것이 더 효율적

[ 사용 ]

  • 연관관계 매핑 어노테이션fetch 옵션 적용 (FetchType.LAZY)

즉시로딩(EAGER)

[ 개념 ]

  • 내부 참조 객체조회 시점에 함께 조회하는 로딩 방법

[ 사용 ]


[ 주의할 점 ]

  • 즉시 로딩으로 발생하는 여러가지 문제점들이 있다
    • 예상하지 못한 SQL 실행
      : JOIN이 많이 발생되기 때문에 훨씬 더 많은 쿼리가 실행됨
    • JPQL에서 N+1문제 발생
      : 앞에서는 em.~()로 우리가 PK를 알 수 있는 상황이었다.
      하지만, JPQLPK를 알 수 없고, 단순히 SQL로 변환되어 실행된다
      --> PK를 모르니까 알기 위해 select query 실행
      --> (데이터가 N개인 테이블(Member)전체 데이터 조회시)
      전체 객체 조회 1번(Member) + 내부에 참조되어 있는 객체 조회 N번(Team)
      --> 나는 그저 select * from member만 원하는데 실제 Team테이블 PK를 포함한 값을 얻기 위한 쿼리들이 N번이나 실행된다
  • 필요에 따라 데이터를 한번에 가져와야 한다면 아래 방법 이용
    • JPQL fetch 조인 --> 보통 사용
    • 엔티티 그래프기능

정리

  • 모든 연관관계에서 지연 로딩을 사용하자
  • 실무에서 즉시 로딩은 사용하지 말자
    • JPQL에서 N+1문제 발생
    • 예상치 못한 쿼리 발생
  • 필요에 따라 한번에 가져와야 한다면 아래 방법 이용
    • JPQL fetch 조인 --> 보통 사용
    • 엔티티 그래프기능
  • @ManyToOne / @OneToOne은 기본이 즉시 로딩
    --> 항상 지연 로딩으로 직접 설정
    해줘야 한다
    (@OneToMany / @ManyToMany는 기본이 지연 로딩)
profile
Developer & PhotoGrapher

0개의 댓글