Closed bielitom closed 1 year ago
For this case, you must provide your own count query via @Query(countQuery = …)
. Spring Data attempts to derive a count query on a best effort basis. Because both, JPQL and SQL allow statements that affect the multiplicity, it is your code that needs to provide a count query if you provided a base query yielding non-distinct results.
As the examples above do not define a countQuery
the natural consequence is that you yield invalid results.
@mp911de
I didn't mention @Query(countQuery = …)
because it may be problematic to maintain with longer queries or using JPA Specification (same results, not included in the example).
When it comes to the getCountQuery
method in /org/springframework/data/jpa/repository/support/SimpleJpaRepository.java:776
it can correctly recognize distinct and non-distinct query. But as mentioned before, the entities returned from the query itself are always unique when JPQL is used.
Anyway, the problem is not really in the count query alone. The problem lies in the getPage
method in PageableExecutionUtils.java
, where the algorithm always thinks, that it is on the last page and totally ignores countQuery
. It happens when the query does not return the full page of the results, because JPA naturally removes duplicates from the results.
because it may be problematic to maintain with longer queries
In such a case, the implementation should go in a custom implementation. Repositories and @Query
have their limits, these aren't all purpose utilities.
Similar to
where the algorithm always thinks, that it is on the last page
We have certain assumptions that we meet. If your queries do not meet these assumptions, then you need to provide either more specific queries or follow the route of implementing a custom fragment.
That being said, we do not intend to introduce more complexity on our side.
By default, all the results of the Repository query are returned as the distinct list. When pagination and joins are used the count of elements differs from the actually obtained results. In some cases it can totally break a pagination, mostly unpredictably.
This JPQL query:
SELECT a FROM Author a INNER JOIN Book b ON b.author = a
will return list of the uniqueAuthor
entities independently from the number of the books per author (assuming, that author have non-zero number of books). On the other hand, the native query will return the list as should be, with the duplicates caused by the join.This behavior basically does not cause the problem, but when it comes to the pagination, it have some consequences.
Page.getTotalElements()
method shows different number than returned number of the entities.The problem looks like to be caused by the condition at
org/springframework/data/support/PageableExecutionUtils.java:66
The condition works with the actual results and if the result list is smaller thanpageSize
it will not runcountQuery
supplier. This causes differentgetTotalElements()
number on the pages wherecontent.size()<getPageSize()
. This can happen on every page because algorithm wrongly thinks, that it is the last page. On the affected pages the number oftotalElements
suddenly drops because it is calculated from the actual offset, not using the count query.To get the acceptable results:
countQuery
every time (you will get the wrong result, but the pagination will remain unchanged on all pages).countQuery
should be used (to not to change the actual behavior). Native queries must be unaffected.How to reproduce
Entities
Repository
Test Code
Related: https://github.com/spring-projects/spring-data-jpa/issues/750