Skip to content

count query fails to be auto generated when using @Query annotation with fetch join #2348

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
sinbom opened this issue Nov 4, 2021 · 6 comments
Assignees
Labels
in: query-parser Everything related to parsing JPQL or SQL type: bug A general bug

Comments

@sinbom
Copy link

sinbom commented Nov 4, 2021

findAllFetch method causes the following error. however, findAllNotFetch method does not cause errors.
because the automatically generated countQuery does not include the fetch keyword. likewise, fetch join paging using @entitygraph does not cause an error.

public interface BoardRepository extends JpaRepository<Board, Long> {

    @Query(value = "SELECT DISTINCT b FROM Board b LEFT JOIN FETCH b.comments ORDER BY b.id")
    Page<Board> findAllFetch(Pageable pageable);
    
    @Query(value = "SELECT DISTINCT b FROM Board b LEFT JOIN b.comments ORDER BY b.id")
    Page<Board> findAllNotFetch(Pageable pageable);

    @EntityGraph(attributePaths = "comments")
    Page<Board> findAll(Pageable pageable);

}
Caused by: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=null,role=com.example.demo.entity.Board.comments,tableName=comment,tableAlias=comments1_,origin=board board0_,columns={board0_.id,className=com.example.demo.entity.Comment}}]
	at org.hibernate.hql.internal.ast.tree.SelectClause.initializeExplicitSelectClause(SelectClause.java:215) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.hql.internal.ast.HqlSqlWalker.useSelectClause(HqlSqlWalker.java:1028) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.hql.internal.ast.HqlSqlWalker.processQuery(HqlSqlWalker.java:796) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:694) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:330) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:278) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:276) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:192) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	... 125 common frames omitted

whether it is fetch join or not does not affect count query results. what is important is the association of the fetching entity.
of course, I know that the count query generated in the findAllNotFetch method has inaccurate paging. but, paging would have been accurate if it was not a collection fetch.

i wonder, the QueryUtils class removes unnecessary queries that do not affect results such as order by, but why the fetch does not. Is this intentional?

public static String createCountQueryFor(String originalQuery, @Nullable String countProjection) {

		Assert.hasText(originalQuery, "OriginalQuery must not be null or empty!");

		Matcher matcher = COUNT_MATCH.matcher(originalQuery);
		String countQuery;

		if (countProjection == null) {

			String variable = matcher.matches() ? matcher.group(VARIABLE_NAME_GROUP_INDEX) : null;
			boolean useVariable = StringUtils.hasText(variable) //
					&& !variable.startsWith(" new") //
					&& !variable.startsWith("count(") //
					&& !variable.contains(","); //

			String complexCountValue = matcher.matches() && StringUtils.hasText(matcher.group(COMPLEX_COUNT_FIRST_INDEX))
					? COMPLEX_COUNT_VALUE
					: COMPLEX_COUNT_LAST_VALUE;

			String replacement = useVariable ? SIMPLE_COUNT_VALUE : complexCountValue;
			countQuery = matcher.replaceFirst(String.format(COUNT_REPLACEMENT_TEMPLATE, replacement));
		} else {
			countQuery = matcher.replaceFirst(String.format(COUNT_REPLACEMENT_TEMPLATE, countProjection));
		}

		return countQuery.replaceFirst(ORDER_BY_PART, "");
	}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Nov 4, 2021
@schauder schauder added in: query-parser Everything related to parsing JPQL or SQL type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged labels Nov 5, 2021
@manousos
Copy link

manousos commented Jan 10, 2022

A temporary workaround is to provide the specific count query.
@sinbom for your example could be the following

    @Query(value = "SELECT DISTINCT b FROM Board b LEFT JOIN FETCH b.comments ORDER BY b.id"
countQuery="SELECT count(b.id) FROM Board b LEFT JOIN  b.comments")
    Page<Board> findAllFetch(Pageable pageable);

@gregturn
Copy link
Contributor

We are working on a parsing solution to better handle such situations than QueryUtils accommodates. For the meantime, we are trying to avoid any more changes to QueryParser.

@gregturn
Copy link
Contributor

With the new parser in place we can opt for SELECT DISTINCT b FROM Board b LEFT JOIN FETCH b.comments ORDER BY b.id to be rendered as either:

SELECT count(DISTINCT b) FROM Board b LEFT JOIN FETCH b.comments

or

SELECT count(DISTINCT b) FROM Board b LEFT JOIN b.comments

Are you saying that Hibernate wants count queries to DROP the FETCH aspect of JOINs for count operations?

@sinbom
Copy link
Author

sinbom commented Mar 16, 2023

SELECT count(DISTINCT b) FROM Board b LEFT JOIN b.comments

Yes, Hibernate wants count queries to DROP the FETCH aspect of JOINs for count operations

@marcioggs
Copy link

marcioggs commented Jul 20, 2023

Any recommended workaround to solve it on JpaSpecificationExecutor.html#findAll(Specification spec, Pageable pageable) at Spring Data JPA 2?

@marcioggs
Copy link

Any recommended workaround to solve it on JpaSpecificationExecutor.html#findAll(Specification spec, Pageable pageable) at Spring Data JPA 2?

For anyone in the same situation, one fragile alternative without using AOP is to override the class org.hibernate.query.criteria.internal.CriteriaQueryImpl and remove the fetch keyword from the queries containing fetch join and count.
This can be done by manipulating the variable jpaqlString at the following line:
https://github.com/hibernate/hibernate-orm/blob/5.6.11/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/CriteriaQueryImpl.java#L301

Be aware that you'd have to update it whenever Hibernate is upgraded on your project to avoid incompatibility issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment