Develop(개발)/Today I Learned

[TIL] QueryDSL의 경로 초기화 문제

seongmik 2025. 2. 16. 14:06
728x90
반응형

매 주 업무를 수행하다보면 배우는 것이 꼭 작더라도 한두개는 생긴다.

이러한 지식들을 나만 알고 넘기기에는 아쉬운 면도 있고, 시간이 지나면 까먹을 가능성도 있기에 TIL을 작성하려한다.

원래는 Github Pages를 이용하여 깃헙 블로그를 만들어서 작성하려 했는데, 이미 티스토리가 방문자 수가 너무 잘 나와서.. ㅎㅎ

아무리 똥글이어도 누군가 읽어주는 게 더 의미있는 글이지 않겠는가! 하는 생각으로 티스토리에 작성하기로 결정했다.

근데 티스토리에서 마크다운 문법만 좀 기존 템플릿하고 호환되게 제공해주면 너무 잘 쓸텐데.. 커스텀 하기도 귀찮고 그런 상태이다.

 

문제 상황

QueryDSL에서 Entity간 서로 필드로 Entity를 가지고 있다면 해당 Entity들의 QClass에도 부모를 타고갈 수 있게 경로가 형성되게 된다.

@Entity
class Event {
    Account account;
}

@Entity
class Account {
    Customer customer;
}

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

위와 같은 Entity 관계가 있다고 가정해보자.

Event는 Account 를 필드로 가지고 있다.

또한 Account는 Customer를 필드로 가지고 있다. 

이런 경우에 QueryDSL에서 Event의 QClass을 가지고 Customer의 Address에 접근할 수 있을까?

당연히 접근은 할 수 있다. 왜냐면 QClass간 경로가 형성되어 있기는 할 것이기 때문이다.

QEvent event = new QEvent("qEvent");

event.account.customer.address.isNotNull();

그렇다면 위와 같은 코드는 따로 별다른 설정이 없을 시에 어떻게 작동할까?

정답은 event.account.customer.address의 isNotNull 메서드에 접근하는 순간 address가 Null이라는 이유로 NullPointException이 발생한다.

초기화 문제로 발생할거면 event.account.customer에서 NPE가 터질 것이지 왜 애매하게 event.account.customer.address에서 발생하는 것일까?

사실 정답은 간단하다. 그냥 최초 경로부터 최대 2번째 깊이의 경로까지만 객체를 초기화하게 QueryDSL이 작동하기 때문이다.

아래는 QueryDSL 레퍼런스 문서의 발췌이다.

해당 처리의 목적에 대해 생각해보자면 QueryDSL 자체적으로 초기화시의 Entity간 순환 참조 문제를 예방하기 위한 목적으로 보인다.

따라서 만약 저렇게 타고 가야한다면 문서에 적힌 것처럼 @QueryInit("깊이가 2이상이더라도 초기화할 경로의 포인트 컷")을 붙여서 명시해주어야한다.

정말 별거 아닌데 모르면 맞아야하는 문제였다.

728x90
반응형