트러블 슈팅 - QueryDSL BoolianBuilder 사용 후 N+1 문제 해결하기

J_log·5일 전
0

트러블 슈팅 - QueryDSL NULL 값 대처하기

저번 트러블 슈팅에서 이어지는 내용이라 위쪽에 링크를 첨부했다.

문제 발생

BooleanBuilder를 이용해 파라미터에 null 값이 들어와도 정상적으로 예약 목록을 조회하도록 구현했지만, 파라미터 2개가 모두 없다면 전체 목록을 조회해오는데 이 부분에서 N+1 문제가 발생했다.

@Override
    public List<Reservation> searchReservations(Long userId, Long itemId) {
        BooleanBuilder predicate = new BooleanBuilder();

        if (userId != null) {
            predicate.and(reservation.user.id.eq(userId));
        }

        if (itemId != null) {
            predicate.and(reservation.item.id.eq(itemId));
        }

        return jpaQueryFactory.selectFrom(reservation)
                .join(reservation.user, user).fetchJoin()
                .join(reservation.item, item).fetchJoin()
                .where(predicate)
                .fetch();
    }

원인 추론

파라미터가 모두 null일 때, Hibernate가 추가적으로 user 테이블을 조회하는 이유는 fetchJoin을 사용했더라도 실제로 데이터를 필요로 하면 Lazy Loading이 발생하기 때문이다.
(연관관계를 설정할 때 fetchType을 Lazy로 설정했기 때문)
이 문제를 해결하려면 파라미터가 둘 다 null인 경우에 대해 별도로 처리를 해줘야 한다.

해결 방법

Reservation 엔티티에서 필요한 필드만 반환하도록 DTO를 사용했다.
이 방법은 모든 데이터를 가져오는 대신, 특정 필드만 조회하여 추가적인 Lazy Loading을 방지한다.

@Override
    public List<ReservationResponseDto> searchReservations(Long userId, Long itemId) {
        BooleanBuilder predicate = new BooleanBuilder();

        if (userId != null) {
            predicate.and(reservation.user.id.eq(userId));
        }
        if (itemId != null) {
            predicate.and(reservation.item.id.eq(itemId));
        }


        return jpaQueryFactory
                .select(Projections.constructor(
                        ReservationResponseDto.class,
                        reservation.id,
                        reservation.user.nickname,
                        reservation.item.name,
                        reservation.status,
                        reservation.startAt,
                        reservation.endAt
                ))
                .from(reservation)
                .join(reservation.user, user)
                .join(reservation.item, item)
                .where(predicate)
                .fetch();
    }

결과 확인

  • 파라미터 2개 모두 값이 있는 경우

  • 쿼리


  • 파라미터 1개만 값이 있는 경우

  • 쿼리


  • 파라미터 2개다 null값인 경우

  • 쿼리

N+1 문제 없이 모든 케이스에서 정상적으로 작동하는 모습을 확인할 수 있었다 !

0개의 댓글