fetch 전략이란 JPA에서 엔티티 간 관계를 맺을 때, 관련된 엔티티를 어떻게 불러올지 결정하는 옵션이다. 엔티티 관계에 따라 기본 fetch 전략이 다르다.
@ManyToOne, @OneToOne : EAGER(즉시)
@OneToMany, @ManyToMany : LAZY(지연)
hibernate:
select
sr1_0.id,
sr1_0.end_time,
sr1_0.fee,
sr1_0.is_deleted,
sr1_0.is_user,
sr1_0.reservation_date,
sr1_0.space_id,
sr1_0.start_time,
sr1_0.user_id
from
space_reservation sr1_0
// 예약 N개에 대해서 연관된 컬럼 정보를 찾아오는 쿼리가 실행된다. (N+1문제)
hibernate:
select
s1_0.id,
s1_0.closing_time,
s1_0.hourly_rate,
s1_0.is_deleted,
s1_0.max_capacity,
s1_0.opening_time,
re1_0.id,
re1_0.dong,
re1_0.jibun_address,
re1_0.road_address,
re1_0.sido,
re1_0.sigungu,
re1_0.floor,
re1_0.has_elevator,
re1_0.has_parking,
h1_0.id,
h1_0.is_deleted,
h1_0.point,
h1_0.user_name,
re1_0.is_deleted,
s1_0.space_description,
s1_0.space_name,
s1_0.space_size,
s1_0.space_type
from
space s1_0
left join
real_estate re1_0
on re1_0.id=s1_0.real_estate_id
left join
host h1_0
on h1_0.id=re1_0.host_id
where
s1_0.id=?
Hibernate:
select
sr1_0.id,
sr1_0.end_time,
sr1_0.fee,
sr1_0.is_deleted,
sr1_0.is_user,
sr1_0.reservation_date,
sr1_0.space_id,
sr1_0.start_time,
sr1_0.user_id
from
space_reservation sr1_0
@Query("SELECT sr FROM SpaceReservation sr " +
"JOIN FETCH sr.space s " +
"JOIN FETCH s.realEstate re " +
"JOIN FETCH re.host")
List<SpaceReservation> findAllReservations();
Hibernate:
select
sr1_0.id,
sr1_0.end_time,
sr1_0.fee,
sr1_0.is_deleted,
sr1_0.is_user,
sr1_0.reservation_date,
s1_0.id,
s1_0.closing_time,
s1_0.hourly_rate,
s1_0.is_deleted,
s1_0.max_capacity,
s1_0.opening_time,
re1_0.id,
re1_0.dong,
re1_0.jibun_address,
re1_0.road_address,
re1_0.sido,
re1_0.sigungu,
re1_0.floor,
re1_0.has_elevator,
re1_0.has_parking,
h1_0.id,
h1_0.is_deleted,
h1_0.point,
h1_0.user_name,
re1_0.is_deleted,
s1_0.space_description,
s1_0.space_name,
s1_0.space_size,
s1_0.space_type,
sr1_0.start_time,
sr1_0.user_id
from
space_reservation sr1_0
join
space s1_0
on s1_0.id=sr1_0.space_id
join
real_estate re1_0
on re1_0.id=s1_0.real_estate_id
join
host h1_0
on h1_0.id=re1_0.host_id
Hibernate:
select
sr1_0.id,
sr1_0.end_time,
sr1_0.fee,
sr1_0.is_deleted,
sr1_0.is_user,
sr1_0.reservation_date,
s1_0.id,
s1_0.closing_time,
s1_0.hourly_rate,
s1_0.is_deleted,
s1_0.max_capacity,
s1_0.opening_time,
re1_0.id,
re1_0.dong,
re1_0.jibun_address,
re1_0.road_address,
re1_0.sido,
re1_0.sigungu,
re1_0.floor,
re1_0.has_elevator,
re1_0.has_parking,
h1_0.id,
h1_0.is_deleted,
h1_0.point,
h1_0.user_name,
re1_0.is_deleted,
s1_0.space_description,
s1_0.space_name,
s1_0.space_size,
s1_0.space_type,
sr1_0.start_time,
sr1_0.user_id
from
space_reservation sr1_0
join
space s1_0
on s1_0.id=sr1_0.space_id
join
real_estate re1_0
on re1_0.id=s1_0.real_estate_id
join
host h1_0
on h1_0.id=re1_0.host_id
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "space_id")
@BatchSize(size = 10)
private Space space;
자바 ORM 표준 JPA PROGRAMMING 책에서 추천하는 방법은 EAGER를 사용하지 말고 LAZY만 사용하라는 것이다. 즉시 로딩 전략은 필요하지 않은 엔티티를 모두 불러오기에 성능 최적화가 어렵다. 따라서 기본값이 @OneToMany, @ManyToMany에서는 LAZY를 사용할 것을 권한다.
N+1 문제를 해결하기 위해 fetch join, batch size, entity graph를 사용할 수 있다. 대규모 데이터를 처리할 때 fetch join은 메모리 부족 문제를 발생시킬 수 있다.