카테고리 없음

Querydsl NPE가 발생할 경우

웅둘 2023. 7. 23. 23:16

개인 프로젝트의 조회 쿼리 작성 중에 NPE에러가 발생하게 됨으로써 이를 해결하기 위해 알게된 내용을 정리하여 작성한 글입니다.

 

 

1. 원인

조회 쿼리를 작성 중에 NPE 에러가 발생하였습니다.

@Override
public List<MyPageHomeDto.ReservationExhibition> getReservationConfirmExhibition(Long userId,
    ReservationStatus status) {
    return queryFactory.selectDistinct(
            Projections.bean(MyPageHomeDto.ReservationExhibition.class, payment.id,
                payment.exhibitionStock.exhibition.preImg, 
                payment.exhiitionStock.exhibition.title,
                payment.reservationStatus, payment.exhibitionStock.exhibition.location,
                payment.exhibitionStock.exhibition.hostUser.nickname.as("hostName"),
                payment.exhibitionStock.startDatetime))
        .from(payment)
        .join(payment.user, user)
        .where(eqStatus(status))
        .orderBy(payment.exhibitionStock.startDatetime.desc())
        .fetch();
}

디버깅을 한 결과  HostUser에서 nickname을 가져와야하는데 HostUser가 Null 이 들어있었습니다.

 

StackOverflow : java.lang.NullPointerException: while filtering data using QueryDsl - Stack Overflow

공식문서 : 3.3. Code generation (querydsl.com)

StackOverflow와 공식문서에 따르면 객체 그래프가 4개 이상 넘어가게 되면 NPE가 발생한다고 합니다.

 

2. 해결

해결방법은 @QueryInit을 사용하는 것입니다.

아래 내용은 공식문서의 내용을 가져와 정리하였습니다.

 

Path initialization

기본적으로 Querydsl은 처음 두 수준의 참조 속성만 초기화합니다.

더 긴 초기화 경로가 필요한 경우 `com.mysema.query.annotations.QueryInit`를 통해 도메인 유형에 어노테이션을 달아야 합니다.

QueryInit은 상세 초기화가 필요한 속성에 사용됩니다. 다음 예에서는 사용법을 보여 줍니다.

@Entity      
class Event {
    @QueryInit("customer.address")
    Account account;
}      

@Entity
class Account{
    Customer customer;    
}

@Entity
class Customer{
    String name;
    Address address;
    // ...
}

 

해당 예시는 Event가 루트 경로/변수로 초기화될 때 account -> customer 경로로 초기화를 하게되어 customer.address 값을 가져올 수 있게 됩니다.

경로 초기화 형식은 와일드카드도 지원합니다.

선언적 형식은 쿼리 유형의 모든 최상위 인스턴스에 적용되고 최종 엔터티 필드의 사용을 가능하게하는 이점이 있습니다.

 

 

나의 쿼리의 경우 Payment <- ExhibitionStock <- Exhibition <- HostUser 이렇게 매핑된 DB 구조였다. 

Payment에서 Hostuser 정보를 얻기 위해서는 관계가 2 depth 이상 깊어지기 때문에 초기화를 해줘야 했다.

이렇게해서 NPE 문제를 해결할 수 있었지만 처음에는 해당 객체에 HostUser를 넣어주지 못했기 때문에 발생했다 생각하여 시간이 오래 소모 되었다.