개요
회사에서 JPQL을 통해 테이블 조인 및 검색 조건을 추가하는 쿼리를 작성하고 있었는데, 쿼리 성능 개선을 위하여 inlineView(from 절 subquery)를 작성해야 할 필요가 있었다.
많은 게시글에서 JPA에서 inlineView를 사용하지 못한다는 글을 봐서, 사용할 수 있는 다양한 샘플과 함께 회사 코드에서는 사용이 어려웠던 이유, Spring Data JPA 레포지토리에 이슈를 생성한 것 까지 모두 작성해본다.
JPA에서 inlineView, 사용가능한가?
사용가능하다. 이전에 작성한 글에서 이미 말했듯이, Hibernate 6.1 버전부터 inlineView가 사용가능 하다.
다만, 다양한 케이스를 테스트 하면서 알게 된 점은 데이터를 Entity, Interface Projection, Class Projection, List 형식 모두 상관없이 쿼리를 작성할 수 있지만,
PageRequest와 함께 inlineView 쿼리를 작성하게 되면 JPA가 제공하는 count 쿼리만으로는 오류가 발생한다.
이 부분에 대해서는 Spring-Data-JPA 레포지토리에 이슈를 작성하여 문제가 맞는것인지, 해결 가능한 문제인지를 천천히 살펴 본 후 공유할 생각이다.
라이브러리를 유지보수 하시는 개발자께서는 이를 countQuery를 직접 작성하는 방법으로 문제를 해결하시길 권유하셨고, 이슈는 closed 상태가 되었다. 아마도 이 부분에 대해서 따로 지원하실 계획은 없는것으로 보인다.
아래에 해당 이슈를 함께 첨부한다.
https://github.com/spring-projects/spring-data-jpa/issues/3662
Support of Inline View with Pageable · Issue #3662 · spring-projects/spring-data-jpa
I found a bug that did not produce a normal count query when using Page with InlineView. It would be good to test it by referring to the example repository. I'm attaching a simple example first. Co...
github.com
JPA에서 inlineView 사용 쿼리
먼저 작성한 테스트 케이스는 다음과 같다.
[Spring JPA][Interface Projection] inline subquery with join - 가능
[Spring JPA][Class Projection] inline subquery with join - 가능
[Spring JPA][Interface Projection] multiple Join inline subquery with join - 가능
[Spring JPA][Interface Projection] multiple Join inline subquery with join and param - 가능
[Spring JPA][Interface Projection] page request without subquery - 가능
[Spring JPA][Interface Projection] page request without join - countQuery 추가 작성 필요
샘플을 직접 확인하고 싶은 경우 아래의 레포지토리에서 샘플을 테스트 할 수 있다.
https://github.com/gogoadl/spring-jpa
GitHub - gogoadl/spring-jpa: spring jpa course
spring jpa course. Contribute to gogoadl/spring-jpa development by creating an account on GitHub.
github.com
Page Request와 함께 InlineView 요청
샘플 쿼리를 디버깅하면서 page타입으로 데이터를 수신할 때에만 Spring-Data-JPA의 AbstractJpaQuery 클래스의 createCountQuery에서 호출이 실패하고 오류 로그가 발생하는 것을 알 수 있었다.
아래는 호출에 실패한 count Query와 오류 로그이다.
SELECT count(T) FROM (SELECT T2.id as id, T2.name as name, T2.address as address FROM Member T2 ) T
org.hibernate.query.SemanticException: The derived SqmFrom[id, name, address] can not be used in a context where the expression needs to be expanded to identifying parts, because a derived model part does not have identifying parts. Replace uses of the root with paths instead e.g. `derivedRoot.get("alias1")` or `derivedRoot.alias1`
생성한 카운트 쿼리를 실제로 실행해봐도 정상적으로 동작하지 않는것을 알 수 있었는데, 위의 카운트 쿼리에서 InlineView 쿼리까지는 정상적으로 동작하지만 결과 테이블을 T라는 alias로 접근 가능하도록 했고 이를 바깥 쪽 SELECT 할 때 alias 테이블 이름만 사용했기 때문에 문제가 발생하는 것으로 보인다. 이를 T.id 처럼 alias와 함께 필드 이름을 적용하면 정상적으로 카운트 쿼리가 동작한다.
마무리
Spring-Data-JPA에 기여하는 것은 실패했다. 내 샘플 코드와 함께 문제를 찾고, 이를 통해 비슷한 문제를 겪었던 사람들에게 도움이 되길 바랬는데.. (Spring 기여자가 되고 싶은 마음이 제일 크긴하다) 아쉽지만 다음 기회를 노려야겠다 ㅎㅎ
결국에는 countQuery를 직접 입력하여 inlineView 쿼리문을 작성할 수 있었고 이를 통해 기존 쿼리 응답의 경우 822.2ms, 수정된 쿼리의 경우 489.9ms 로 67.83%의 성능 개선을 얻을 수 있었다
성능 개선율은 아래의 위키 페이지를 참고했다.
https://zetawiki.com/wiki/%EC%84%B1%EB%8A%A5_%EA%B0%9C%EC%84%A0%EC%9C%A8
'Spring' 카테고리의 다른 글
Spring Data JPA - From 절 Subquery (InlineView)사용하기 (5) | 2024.11.05 |
---|---|
Spring Boot에서 Runtime Logging Level 변경하기 (1) | 2024.09.03 |
Spring Boot 서버 포트 변경하기 (1) | 2023.10.03 |