Eager fetch, Lazy fetch

진성대·2023년 3월 20일
0

JPA

목록 보기
2/3

Eager fetch, Lazy fetch

참고

[JPA] 즉시 로딩과 지연 로딩(FetchType.LAZY or EAGER)

Fetch?

애플리케이션이 DB로 부터 데이터를 가져오는 것

DB와 통신하여 데이터를 읽는 것에는 큰 비용이 소모되기 때문에, 똑똑하게 가져오는 전략이 필요

  • eager : 프로그램 코드가 쿼리를 날리는 시점에 데이터를 즉시 가져오기
    • ex : select a.id from A a inner join B on a.b_id = b.id (b를 보지 않았지만 일단 다 가져옴)
  • lazy : 가져오려는 데이터를 애플리케이션에서 실제로 접근할 때 가져오기
    • ex : select a.id from A; (select b from B b where b.id = ?)
  • lazy 전략은 기본적으로
    • ORM 의 특징이자 기능적 장점
    • 더 빠르고 경제적인 쿼리 (적절히만 사용한다면)
    • 잘못 사용하면 데이터 접근 에러 (ex : LazyInitializationException)

각 JPA 연관관계 애노테이션은 기본 fetch 전략을 가지고 있다.

기본 세팅의 핵심은 “어느 쪽이 효율적인가”

  • @OneToOne: FetchType.EAGER
  • @ManyToOne: FetchType.EAGER
  • @OneToMany: FetchType.LAZY
  • @ManyToMany: FetchType.LAZY

fetch 전략의 설정 (실전)

효율성 - 데이터가 어느 쪽으로 더 자주 사용될 것 같은가 예측

  • default 내버려두기 : 필요한 시점에 최선의 방식으로 데이터를 가져옴
  • LAZY 사용 : 연관 관계가 있는 엔티티에서 자식 엔티티만 가져오는 시나리오일 때
    • 프로그래머가 로직 흐름에서 join을 의식하고 있지 않음
    • LAZY 세팅이 후속 쿼리 발생 방지를 보장하지는 않음
      • exL 불러들인 자식 엔티티가 서비스 레이어 어딘가에서 결국 부모 엔티티 필드를 견드렸을 경우
  • EAGER 사용 : 연관 관계가 있는 엔티티에서 무조건 다 가져오는 시나리오일 때
    • 프로그래머가 join을 사용해야 하는 상황임을 인지하고 있음
    • EAGER 세팅이 join 동작을 보장하지는 않음
      • ex : Spring Data JPA 쿼리 메소드 findAll()
      • JPQL 을 직접 작성해서 join 을 영속성 컨텍스트에 알려줘야 함 (ex : querydsl)

N + 1 query problem

  • 나는 한 번 쿼리를 날렸을 뿐인데, 1 + N개의 쿼리가 더 생겼다.

Join, Fetch Join 차이

  • 일반 Join
    • Fetch Join과 달리 연관 Entity에 Join을 걸어도 실제 쿼리에서 SELECT 하는 Entity는오직 JPQL에서 조회하는 주체가 되는 Entity만 조회하여 영속화
    • 조회의 주체가 되는 Entity만 SELECT 해서 영속화하기 때문에 데이터는 필요하지 않지만 연관 Entity가 검색조건에는 필요한 경우에 주로 사용됨
  • Fetch Join
    • 조회의 주체가 되는 Entity 이외에 Fetch Join이 걸린 연관 Entity도 함께 SELECT 하여 모두 영속화
    • Fetch Join이 걸린 Entity 모두 영속화하기 때문에 FetchType이 Lazy인 Entity를 참조하더라도이미 영속성 컨텍스트에 들어있기 때문에 따로 쿼리가 실행되지 않은 채로 N+1문제가 해결됨

3가지 방법

  • 똑똑한 Lazy
    • 비지니스 로직을 면밀히 분석하여, 불필요한 연관 관계 테이블 정보를 불러오는 부분을 제거
    • 가장 똑똑하고 효율적인 방법
  • eager fetch + join jpql
    • join 쿼리를 직접 작성하는 방법은 다양(@Querym querydsl, …)
    • 쿼리 한 번에 오긴 하겠지만, join 쿼리 연산 비용과 네트워크로 전달되는 데이터가 클 수 있음
  • 후속 쿼리를 in 으로 묶어주기 : N + 1 → 1 + 1 로 I/O 줄일 수 있음
    • 하이버네이트 프로퍼티 : default_batch_fetch_size
    • 스프링 부트에서 쓰는 법: spring.jpa.properties.hibernate.default_batch_fetch_size
    • 100 ~ 1000 사이클 추천
    • 모든 쿼리에 적용되고, 복잡한 도메인에서 join 쿼리를 구성하는 것이 골치 아플때 효과적
profile
신입 개발자

0개의 댓글