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 쿼리를 구성하는 것이 골치 아플때 효과적