들어가기 전에
해당 포스팅은 아래 환경 기준으로 작성되었습니다.
JDK 17
Spring Boot 3.1.0
JPA + QueryDsl 5.0.0
Hibernate 6.2.2
개요
QueryDsl로 쿼리를 작성하던 중 transform을 사용하던 곳에서 아래와 같은 오류가 발생했다.
쿼리를 작성하는 부분은 아무리 봐도 문제가 없고 이전 버전에서 잘 동작하던 쿼리이기도 했다.
코드를 들어가 보니 hibernate 6.x 버전으로 넘어오면서 ScrollableResults에서 get(int) 메소드가 사라져서 발생한 문제였다.
해당 이슈를 구글링해 보니 아래와 같은 해결책이 존재했다.
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(JPQLTemplates.DEFAULT, entityManager);
}
위 설정을 적용하니 transform 부분에서의 오류는 해결이 되었는데,
대신 CaseBuilder와 sum()을 같이 사용하는 쿼리에서 아래와 같은 오류가 발생했다.
해당 이슈는 구글링해도 비슷한 사례가 나오지 않았다.
위의 해결책을 적용하고 나서 발생한 오류이다 보니 간단하게는 각 쿼리에서 JPAQueryFactory를 다르게 적용해 주면 해결을 할 수 있겠지만,
쿼리마다 다르게 적용하는 부분이 번거롭기 때문에 두 이슈를 동시에 해결할 수 있는 방안을 강구하였고,
해결책을 찾게 되어 포스팅하게 되었다.
원인
기본적으로 Hibernate 5 버전 이상에서는 QueryDsl 템플릿으로 Hibernate5Templates
를 사용한다.
Hibernate5Templates
은 HQLTemplates
을 상속받은 클래스인데,
이 HQLTemplates
에서 QueryHandler로 HibernateHandler
를 사용한다.
transform에서의 이슈가 이 HibernateHandler
를 통해 발생한다.
또한 기본적인 JPQLTemplates
은 QueryHandler로 DefaultQueryHandler
를 사용하므로 위의 해결책에서 기술했듯 JPAQueryFactory에서 JPQLTemplates.DEFAULT
를 적용하여 QueryHandler를 DefaultQueryHandler
로 바꿔주어 해결한다.
하지만 위의 방식은 QueryHandler만 바꾸는 것이 아닌 템플릿 자체를 바꾸는 것인데,
템플릿을 JPQLTemplates
로 바꾸니 CaseBuilder + sum() 등에서 오류가 발생하는 것으로 보아 JPQLTemplates
은 Hibernate 관련하여 제대로 대응을 하지 못하는 것으로 추측할 수 있겠다.
해결 방안
해결 방안은 의외로 간단하다. Hibernate5Templates
를 그대로 사용하면서 QueryHandler만 DefaultQueryHandler
로 바꿔주면 되겠다.
다만 JPAQueryFactory에서 QueryHandler만 바꿔줄 수는 없으므로 Hibernate5Templates
를 확장한 커스텀 클래스를 생성하여 적용해야 한다.
public class CustomHibernate5Templates extends Hibernate5Templates {
@Override
public QueryHandler getQueryHandler() {
return DefaultQueryHandler.DEFAULT;
}
}
JPQLTemplates에서는 getQueryHandler()
메소드를 통해 QueryHandler를 불러와서 사용하므로 해당 메소드를 Override 하여 DefaultQueryHandler
를 적용한다.
커스텀 클래스를 생성했다면 아래와 같이 JPAQueryFactory에 연결해 주자.
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(new CustomHibernate5Templates(), entityManager);
}
위와 같이 적용하고 나면 위 두 이슈 모두 해결된 것을 확인할 수 있다.
마무리
해결된 코드 자체는 간단하지만 이런 경우는 라이브러리 코드를 뜯어보고 커스텀해야 하는 작업이라 원인 파악 및 해결하기까지 시간이 오래 걸린다.
워낙 구조가 복잡해서 적용된 부분 및 원인에 대해서 일일이 기록하지는 않았지만 필자 또한 해당 이슈 해결을 위해 계속 디버깅해 보고 원인을 추측하는 데 적지 않은 시간 소요가 발생했다.
개발을 하다 보면 이런 경우가 종종 발생할 수 있는데 라이브러리 내부에 들어가서 코드를 읽으며 정확한 동작 원리를 이해하는 것이 중요하다는 것을 다시금 느낀다.
ScrollableResults에서 get(int) 메소드가 사라지지 않았다면 이런 일은 없지 않았을까..?
참고 링크
https://github.com/querydsl/querydsl/issues/3428
'Java & Spring' 카테고리의 다른 글
Spring Boot 무료로 배포하기 (Koyeb, GitHub) (0) | 2024.03.21 |
---|---|
[Spring] eventListener, transactionalEventListener 예외 및 트랜잭션 전파 총정리 (0) | 2023.12.29 |
[Spring] JPA 다중 서버 환경 DB 동시성 문제 해결하기 (2) | 2023.08.18 |
[JAVA] 내부 resource 파일 활용하기 (0) | 2023.07.29 |
[JAVA] LocalDate로 한국식 월 주차 구하기 (6) | 2023.07.27 |