TIL - Hibernate.query ParsingException

su·2023년 8월 1일
0

TIL

목록 보기
55/93
post-thumbnail

문제 - JPAQuery를 활용한 Test 코드 실행 중 오류

1) 문제

JPAQuery를 활용해서 클래스를 만들고, Test 코드를 작성한 후 테스트를 진행했다.
그런데 .. ㅎㅎ 오류가 발생했다.
처음 보는 오류라 당황했는데, hibernate에서 발생한 오류 같았다.

org.hibernate.query.sqm.ParsingException: line 2:18 mismatched input 'thread'
expecting {<EOF>, ',', CROSS, FULL, GROUP, INNER, JOIN, LEFT, ORDER, OUTER, RIGHT, WHERE}
org.springframework.dao.InvalidDataAccessApiUsageException:
org.hibernate.query.sqm.ParsingException: line 2:18 mismatched input 'thread'
expecting {<EOF>, ',', CROSS, FULL, GROUP, INNER, JOIN, LEFT, ORDER, OUTER, RIGHT, WHERE}

chatGPT에게 물어보니, Hibernate 쿼리를 파싱하는 도중에 발생한 예외라고 한다.
Hibernate Query Language 쿼리를 파싱하는 도중, 잘못된 입력을 발견했다고 나와있다고 한다.

문제가 되는 위치는 2번째 줄의 18번째 문자이며, 해당 위치에 thread가 예상되지만, 허용되는 문자는 {<EOF>, ',', CROSS, FULL, GROUP, INNER, JOIN, LEFT, ORDER, OUTER, RIGHT, WHERE}
라고 한다.

2) 시도

우선은 테스트 코드에 오류가 있는지 살펴보았다.
테스트 코드에서 혹시 잘못 작성한 게 있나 싶었는데, 문제는 없었다.
그리고 ThreadRepositoryQuery를 interface로 만들어 상속받아 사용하는 ThreadRepositoryQueryImpl 클래스가 있었는데, 이 안에 메소드들에 문제가 없는지도 확인했다.

public class ThreadRepositoryImpl implements ThreadRepositoryQuery {

	private final JPAQueryFactory jpaQueryFactory;
    
    public Page<Thread> search(ThreadSerchCond cond, Pageable pageable) {
    	...
        ...
        
        long totalSize = countQuery(cond).fetch().get(0);
        
        ...
    }
    
    public JPAQuery<Long> countQuery(ThreadSearchCond cond) {
    	return jpaQueryFactory.select(Wildcard.count)
    	.from(thread)
        .fetchJoin()
        .where(
        	channelIdEq(cond.getChannelId()),
            mentionedUserIdEq(cond.getMentionedUserId())
        );
    }
     
    ...

}

코드를 다시 보고 Test 코드를 재실행해봤으나 결과는 똑같았다.

3) 해결

는 countQuery 메소드에 문제가 있었다 .. ㅎ
return 문의 세 번째 줄의 fetchJoin() 이 문제였다.
저 코드를 지우고 다시 실행하니 오류가 없이 제대로 실행되었다.

private JPAQuery<Long> countQuery(ThreadSearchCond cond) {
	return jpaQueryFactory.select(Wildcard.count)
    	.from(thread)
        .fetchJoin()
        .where(
        	channelIdEq(cond.getChannelId()),
            mentionedUserIdEq(cond.getMentionedUserId())
        );
}

4) 배운 점

.fetchJoin() 은 JPA의 특정 쿼리 메소드를 사용할 때, 연관된 엔티티들을 한 번의 쿼리로 모두 가져오는 기능이다.
주로 엔티티 간 연관 관계에서 N+1 쿼리 문제 해결에 사용된다.

N+1 쿼리 문제는 한 개의 쿼리로 기본 엔티티를 가져오고, 해당 엔티티와 연관된 다른 엔티티를 가져오기 위해서 추가적인 쿼리가 발생하는 상황이다.
예를 들어, 하나의 쓰레드를 가져온 뒤에, 그 쓰레드에 대한 댓글 목록을 가져오는 경우라면, 기본 쓰레드 쿼리 한 번과 댓글 쿼리 N번이 발생해 성능에 영향을 미칠 수 있는 것이다.
그 문제를 .fetchJoin()이 해결해준다 !

다만, fetchJoin()을 사용하면서 주의해야할 점은, 관련된 엔티티들을 함께 로딩하므로 엔티티의 수가 많고 복잡하다면 쿼리 결과가 불필요하게 커질 가능성이 있다.
또한 지연 로딩된 데이터를 강제로 로딩하기 때문에, 성능 문제를 가져올 수도 있다.

그렇다면, 내 경우의 Test 코드에 오류가 발생했던 이유는 ..?
.fetchJoin()을 사용하면 관련 엔티티를 로딩하는데, 테스트 환경에서는 원하는 데이터가 없을 수도 있고, 이로 인해 오류가 발생할 수 있다고 한다 ..!
(테스트 환경에서 테스트 데이터가 충분히 준비되지 않아서 일 수 있다.)

profile
(❁´◡`❁)

0개의 댓글