[문제해결] Querydsl을 적용한 Fetch Join

YeonJi·2025년 9월 16일
0

문제해결

목록 보기
24/24

💭 상황

Fetch Join을 했는데도 추가 쿼리가 생성되는 문제가 발생하고 있다.

...

    return Optional.ofNullable(jpaQueryFactory
            .selectFrom(reservation)
            .innerJoin(user).on(reservation.user.id.eq(user.id)).fetchJoin()
            .innerJoin(ticketSchedule).on(reservation.ticketSchedule.id.eq(ticketSchedule.id)).fetchJoin()
            .where(conditions)
            .fetchOne());
    }
Hibernate: 
    select
        r1_0.id,
        r1_0.cancelled_at,
        r1_0.created_at,
        r1_0.reservation_name,
        r1_0.reservation_number,
        r1_0.reservation_phone,
        r1_0.status,
        r1_0.ticket_schedule_id,
        r1_0.total_amount,
        r1_0.total_quantity,
        r1_0.updated_at,
        r1_0.user_id 
    from
        reservation r1_0 
    join
        user u1_0 
            on r1_0.user_id=u1_0.id 
    join
        ticket_schedule ts1_0 
            on r1_0.ticket_schedule_id=ts1_0.id 
    where
        r1_0.id=? 
        and u1_0.email=?
Hibernate: 
    select
        ts1_0.id,
        ts1_0.created_at,
        ts1_0.is_active,
        ts1_0.quantity,
        ts1_0.remaining_quantity,
        ts1_0.start_date,
        ts1_0.start_time,
        ts1_0.ticket_id,
        ts1_0.updated_at 
    from
        ticket_schedule ts1_0 
    where
        ts1_0.id=?

💥 원인

연관 관계 매핑이 되었는데도 Fetch Join이 올바르게 적용되지 않았던 이유는 on 조건으로 Join을 하였기 때문이다.
Fetch Join은 Select된 루트 Entity와 직접 관계가 매핑되었을 때만 동작한다.

 .innerJoin(user).on(reservation.user.id.eq(user.id)).fetchJoin()
 .innerJoin(ticketSchedule).on(reservation.ticketSchedule.id.eq(ticketSchedule.id)).fetchJoin()

JPQL을 사용해 Fetch Join을 했을 때처럼 on 조건을 사용하지 않으면 된다.

@Query("select r from Reservation r inner join fetch r.user u where u.email = :email")
List<Reservation> findAllByEmail(String email);

또한, 연관된 Entity를 한 번에 가져오기 위해 Fetch Join을 사용하는 것인데 DTO projection을 사용한 경우에는 DTO를 바로 가져오기 때문에 Fetch Join을 사용하는 의미가 없다.

@Getter
public class PartnerSimpleResDto {

    private final Long userId;
    private final Long partnerId;
    private final String partnerName;
    private final String businessNumber;
    private final LocalDateTime createdAt;

    @QueryProjection
    public PartnerSimpleResDto(Long userId, Long partnerId, String partnerName, String businessNumber, LocalDateTime createdAt) {
        this.userId = userId;
        this.partnerId = partnerId;
        this.partnerName = partnerName;
        this.businessNumber = businessNumber;
        this.createdAt = createdAt;
    }
}
.select(new QPartnerSimpleResDto(
        user.id,
        partner.id,
        user.displayName,
        partner.businessNumber,
        user.createdAt
))
.from(partner)
.innerJoin(user).on(partner.user.id.eq(user.id)).fetchJoin()

⭕ 해결

.innerJoin(reservation.user, user).fetchJoin()
.innerJoin(reservation.ticketSchedule, ticketSchedule).fetchJoin()
Hibernate: 
    select
        r1_0.id,
        r1_0.cancelled_at,
        r1_0.created_at,
        r1_0.reservation_name,
        r1_0.reservation_number,
        r1_0.reservation_phone,
        r1_0.status,
        r1_0.ticket_schedule_id,
        ts1_0.id,
        ts1_0.created_at,
        ts1_0.is_active,
        ts1_0.quantity,
        ts1_0.remaining_quantity,
        ts1_0.start_date,
        ts1_0.start_time,
        ts1_0.ticket_id,
        ts1_0.updated_at,
        r1_0.total_amount,
        r1_0.total_quantity,
        r1_0.updated_at,
        r1_0.user_id,
        u1_0.id,
        u1_0.created_at,
        u1_0.display_name,
        u1_0.email,
        u1_0.is_deleted,
        u1_0.password,
        u1_0.phone,
        u1_0.role,
        u1_0.updated_at 
    from
        reservation r1_0 
    join
        user u1_0 
            on u1_0.id=r1_0.user_id 
    join
        ticket_schedule ts1_0 
            on ts1_0.id=r1_0.ticket_schedule_id 
    where
        r1_0.id=? 
        and u1_0.email=?
profile
문제해결 위주로 기록

0개의 댓글