QueryDsl) fetchResults()가 deprecated 된 이유

Dokuny·2022년 1월 18일
1

QueryDsl로 페이징 쿼리를 작성하는데 .fetchResults()가 deprecated 되었다고 표시되었길래 찾아보았다.

deprecated 된 이유 전문

fetchResults requires a count query to be computed. 
In querydsl-sql, this is done by wrapping the query in a subquery, like so: SELECT COUNT(*) FROM (<original query>).
Unfortunately, JPQL - the query language of JPA - does not allow queries to project from subqueries. 
As a result there isn't a universal way to express count queries in JPQL. 
Historically QueryDSL attempts at producing a modified query to compute the number of results instead. 
However, this approach only works for simple queries.
Specifically queries with multiple group by clauses and queries with a having clause turn out to be problematic.
This is because COUNT(DISTINCT a, b, c), while valid SQL in most dialects, is not valid JPQL. 
Furthermore, a having clause may refer select elements or aggregate functions and therefore cannot be emulated by moving the predicate to the where clause instead.
In order to support fetchResults for queries with multiple group by elements or a having clause, we generate the count in memory instead.
This means that the method simply falls back to returning the size of fetch(). 
For large result sets this may come at a severe performance penalty.
For very specific domain models where fetchResults() has to be used in conjunction with complex queries containing multiple group by elements and/or a having clause, we recommend using the Blaze-Persistence  integration for QueryDSL.
Among other advanced query features, Blaze-Persistence makes it possible to select from subqueries in JPQL.
As a result the BlazeJPAQuery provided with the integration, implements fetchResults properly and always executes a proper count query.
Mind that for any scenario where the count is not strictly needed separately, we recommend to use fetch() instead.

queryDsl의 fetchResult의 경우 count를 하기위해선 count용 쿼리를 만들어서 실행해야 하는데, 카운트를 하려는 select 쿼리를 기반으로 count 쿼리를 만들어 실행한다.
위의 전문을 보면 이런 식인 것 같다.
SELECT COUNT(*) FROM (<original query>).

그런데 이게 단순한 쿼리에서는 잘 동작하는데, 복잡한 쿼리(다중그룹 쿼리)에서는 잘 작동하지 않는다고 한다.

찾아보니 groupby having 절을 사용하는 등의 복잡한 쿼리 문에서 예외가 떠버리는 듯.

더불어 대부분의 dialect에서는 count쿼리가 유효하지만 JPQL에서는 아니란다. 더 찾아보니 모든 dialect에서 지원하는 것도 아니라고 한다.

그렇기 때문에 카운트하려면 그냥 fetch() 를 쓰고 따로 자바쪽에서 count를 세서 사용하라는 것 같다.

This means that the method simply falls back to returning the size of fetch(). 

근데 이렇게 해도 내가 공부하던 코드에서는 size()는 단순히 그 List의 크기만 가져와서 총 total count는 제대로 계산이 안되는 것 같다.

그렇기 때문에 따로 count query를 날리는게 좋아보인다.

예시

public Page<User> findUserWithPaging(Pageable pageable) {

	List<User> content = queryFactory
			.selectFrom(user)
			.where(user.username.like("user_"))
			.offset(pageable.getOffset()) // offset
			.limit(pageable.getPageSize()) // limit
			.fetch(); // fetchResults() 대신 fetch 사용 
	
    	// List 의 size 메소드로 카운트를 넘겨준다.
	return new PageImpl<>(content, pageable, content.size()); 
}

Reference

profile
모든 것은 직접 경험해보고 테스트하자

0개의 댓글