보통 두개의 테이블에서 N+1 문제 발생시 Left Join을 통해서 해결을 한다.
API 제작 중에 3개의 테이블을 Left Join해서 N+1문제를 해결하고자 하였으나 쿼리문을 3개 발생시켜 이를 해결해서 쿼리문의 양을 줄이고 DB 서버에 부하를 줄이고자 하였다.
API
API를 간단하게 설명하면 사용자가 음식점에서 예약을 하면 예약이 DB에 영속화 된다. 이 후 정보 요청시 예약만한 경우에는 예약 정보만을 출력하지만 사용자가 해당 예약정보에 맞는 주문까지 하고 정보 요청시 총 3가지의 정보가 들어가게 된다.
기존 코드
해당 코드처럼 작성시 총 3개의 쿼리문이 발생한다 N+1문제는 발생하지 않지만 두개의 left join이 제대로 처리되지 않는다. 예를 들어, 1번 테이블과 2번테이블은 1대1, 2번테이블과 3번테이블은 1대N일 때, 2번과 3번에서 N+1이 발생하지는 않지만 하나하나 쿼리문을 불러 총 3번의 쿼리문을 발생시킨다.
StoreReservationEntity result = queryFactory
.select(storeReservationEntity)
.from(storeReservationEntity)
.leftJoin(storeReservationEntity.child, storeOrdersEntity)
.leftJoin(storeOrdersEntity.childSet, storeOrdersMapEntity)
.where(storeReservationEntity.storeName.eq(storeName)
.and(storeReservationEntity.userId.eq(userId)))
.fetchFirst();
쿼리문
Hibernate:
select
s1_0.reservation_id,
s1_0.created_at,
s1_0.date,
s1_0.store_name,
s1_0.store_table,
s1_0.time,
s1_0.updated_at,
s1_0.user_id
from
reservation s1_0
left join
store_orders c1_0
on s1_0.reservation_id=c1_0.reservation_id
left join
store_orders_map c2_0
on c1_0.orders_id=c2_0.orders_id
where
s1_0.store_name=?
and s1_0.user_id=? limit ?
Hibernate:
select
s1_0.orders_id,
s1_0.reservation_id
from
store_orders s1_0
where
s1_0.reservation_id=?
Hibernate:
select
c1_0.orders_id,
c1_0.order_id,
c1_0.food_count,
c1_0.food_name
from
store_orders_map c1_0
where
c1_0.orders_id=?
변경 코드
- fetchJoin() 메서드를 사용해서 해결하였다. 이는 3개 이상의 조인에서 사용가능하며 3개 이상의 테이블이 하나의 쿼리문으로 Lazy Loading을 피하게 도와준다.
StoreReservationEntity result = queryFactory
.select(storeReservationEntity)
.from(storeReservationEntity)
.leftJoin(storeReservationEntity.child, storeOrdersEntity).fetchJoin()
.leftJoin(storeOrdersEntity.childSet, storeOrdersMapEntity).fetchJoin()
.where(storeReservationEntity.storeName.eq(storeName)
.and(storeReservationEntity.userId.eq(userId)))
.fetchFirst();
쿼리문
Hibernate:
select
s1_0.reservation_id,
c1_0.orders_id,
c2_0.orders_id,
c2_0.order_id,
c2_0.food_count,
c2_0.food_name,
s1_0.created_at,
s1_0.date,
s1_0.store_name,
s1_0.store_table,
s1_0.time,
s1_0.updated_at,
s1_0.user_id
from
reservation s1_0
left join
store_orders c1_0
on s1_0.reservation_id=c1_0.reservation_id
left join
store_orders_map c2_0
on c1_0.orders_id=c2_0.orders_id
where
s1_0.store_name=?
and s1_0.user_id=?
'Trouble Shooting > Data API' 카테고리의 다른 글
Query DSL, Entity대신 DTO로 받기 (0) | 2023.06.08 |
---|---|
Querydsl 적용과 고찰 (0) | 2023.06.05 |
SELECT + JOIN 개선에 대한 고찰 (0) | 2023.05.15 |