저번 트러블 슈팅에서 이어지는 내용이라 위쪽에 링크를 첨부했다.
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();
}
N+1 문제 없이 모든 케이스에서 정상적으로 작동하는 모습을 확인할 수 있었다 !