Skip to content
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

FetchableFluentQueryByPredicate does not respect project() call on queryFunction #2820

Closed
MahmoudFawzyKhalil opened this issue Feb 23, 2023 · 3 comments
Assignees
Labels
in: repository Repositories abstraction type: bug A general bug

Comments

@MahmoudFawzyKhalil
Copy link

MahmoudFawzyKhalil commented Feb 23, 2023

        return postRepository.findBy(QPost.post.content.toLowerCase().like(text), q -> q
                .project("comments")
                .page(PageRequest.of(
                        0, 10)
                ));

When calling a findBy() method that takes a Querydsl Predicate and a queryFunction, when you pass in the properties you want to fetch using the entity graph and then call page(), your project() call is ignored because no query hints are set on the query in the FetchableFluentQueryByPredicate implementation.

The implementation of methods like all() in FetchableFluentQueryByPredicate construct an EntityGraph and set it as a query hint to the JPA query, but this does not happen for the page() method.

Is there a reason why calls to project() are not respected when calling page(), or is this a bug?

And if there is a reason, maybe this should be indicated in the documentation of the methods or an exception should be thrown?

Please note a similar issue was opened before but was closed due to a lack of minimal working example: #2477

Here is a link to a working minimal example:
https://github.com/MahmoudFawzyKhalil/spring-data-jpa-paged-projection-issue/blob/master/src/main/java/com/example/SpringDataJpaPagedProjectionIssueApplication.java

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Feb 23, 2023
@schauder
Copy link
Contributor

Almost a duplicate of #2721 which is basically the same issue for Specification.

@gregturn
Copy link
Contributor

At a glance, it appears that oneValue, firstValue, all, and stream all invoke createSortedAndProjectedQuery, which has:

private AbstractJPAQuery<?, ?> createSortedAndProjectedQuery() {

	AbstractJPAQuery<?, ?> query = finder.apply(sort);

	if (!properties.isEmpty()) {
		query.setHint(EntityGraphFactory.HINT, EntityGraphFactory.create(entityManager, entityType, properties));
	}

	return query;
}

Here you can see the query hint being applied.

However, page() does NOT do this:

private Page<R> readPage(Pageable pageable) {

	AbstractJPAQuery<?, ?> pagedQuery = pagedFinder.apply(sort, pageable);

	List<R> paginatedResults = convert(pagedQuery.fetch());

	return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> countOperation.apply(predicate));
}

Now I looked at FetchableFluentQueryBySpecification for comparison, and since the APIs are different between JPA and Querydsl, it's clear that the Specification-based counterpart has one method to build the basic query, ensuring that is has query hints applied. Our Querydsl-based version clearly does not.

I took a first cut at adding this missing clause to the readPage operation up above and checked the logs. This is what I see.

Without the hints:

2023-02-24 17:37:30,586 DEBUG         org.hibernate.orm.sql.ast.create:  55 - Registration of TableGroup [StandardVirtualTableGroup(org.springframework.data.jpa.domain.sample.User(user).address)] with identifierForTableGroup [org.springframework.data.jpa.domain.sample.User.address] for NavigablePath [org.springframework.data.jpa.domain.sample.User.address] 
2023-02-24 17:37:30,586 TRACE persister.entity.AbstractEntityPersister:6520 - #findSubPart(`firstname`)
2023-02-24 17:37:30,586 DEBUG e.query.sqm.sql.BaseSqmToSqlAstConverter:5089 - Determining mapping-model type for SqmParameter : SqmPositionalParameter(1)
2023-02-24 17:37:30,586 DEBUG e.query.sqm.sql.BaseSqmToSqlAstConverter:4981 - Determining mapping-model type for SqmPath : SqmBasicValuedSimplePath(org.springframework.data.jpa.domain.sample.User(user).firstname) 
2023-02-24 17:37:30,586 TRACE persister.entity.AbstractEntityPersister:6520 - #findSubPart(`firstname`)
2023-02-24 17:37:30,586 DEBUG e.query.sqm.sql.BaseSqmToSqlAstConverter:4981 - Determining mapping-model type for SqmPath : SqmBasicValuedSimplePath(org.springframework.data.jpa.domain.sample.User(user).firstname) 
2023-02-24 17:37:30,586 TRACE persister.entity.AbstractEntityPersister:6520 - #findSubPart(`firstname`)
2023-02-24 17:37:30,587 DEBUG      org.hibernate.orm.results.graph.AST:  69 - DomainResult Graph:
 \-EntityResultImpl [org.springframework.data.jpa.domain.sample.User(user)]
 |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).active]
 |  +-EmbeddableFetchImpl [org.springframework.data.jpa.domain.sample.User(user).address]
 |  |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).address.city]
 |  |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).address.country]
 |  |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).address.streetName]
 |  |  \-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).address.streetNo]
 |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).age]
 |  +-DelayedCollectionFetch [org.springframework.data.jpa.domain.sample.User(user).attributes]
 |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).binaryData]
 |  +-DelayedCollectionFetch [org.springframework.data.jpa.domain.sample.User(user).colleagues]
 |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).createdAt]
 |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).dateOfBirth]
 |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).emailAddress]
 |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).firstname]
 |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).lastname]
 |  +-EntityDelayedFetchImpl [org.springframework.data.jpa.domain.sample.User(user).manager]
 |  \-DelayedCollectionFetch [org.springframework.data.jpa.domain.sample.User(user).roles]

WITH the hints:

2023-02-24 17:36:34,305 DEBUG         org.hibernate.orm.sql.ast.create:  55 - Registration of TableGroup [StandardVirtualTableGroup(org.springframework.data.jpa.domain.sample.User(user).address)] with identifierForTableGroup [org.springframework.data.jpa.domain.sample.User.address] for NavigablePath [org.springframework.data.jpa.domain.sample.User.address] 
2023-02-24 17:36:34,306 TRACE persister.entity.AbstractEntityPersister:6520 - #findSubPart(`firstname`)
2023-02-24 17:36:34,306 DEBUG e.query.sqm.sql.BaseSqmToSqlAstConverter:5089 - Determining mapping-model type for SqmParameter : SqmPositionalParameter(1)
2023-02-24 17:36:34,306 DEBUG e.query.sqm.sql.BaseSqmToSqlAstConverter:4981 - Determining mapping-model type for SqmPath : SqmBasicValuedSimplePath(org.springframework.data.jpa.domain.sample.User(user).firstname) 
2023-02-24 17:36:34,306 TRACE persister.entity.AbstractEntityPersister:6520 - #findSubPart(`firstname`)
2023-02-24 17:36:34,306 DEBUG e.query.sqm.sql.BaseSqmToSqlAstConverter:4981 - Determining mapping-model type for SqmPath : SqmBasicValuedSimplePath(org.springframework.data.jpa.domain.sample.User(user).firstname) 
2023-02-24 17:36:34,306 TRACE persister.entity.AbstractEntityPersister:6520 - #findSubPart(`firstname`)
2023-02-24 17:36:34,306 DEBUG      org.hibernate.orm.results.graph.AST:  69 - DomainResult Graph:
 \-EntityResultImpl [org.springframework.data.jpa.domain.sample.User(user)]
 |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).active]
 |  +-EmbeddableFetchImpl [org.springframework.data.jpa.domain.sample.User(user).address]
 |  |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).address.city]
 |  |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).address.country]
 |  |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).address.streetName]
 |  |  \-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).address.streetNo]
 |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).age]
 |  +-DelayedCollectionFetch [org.springframework.data.jpa.domain.sample.User(user).attributes]
 |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).binaryData]
 |  +-DelayedCollectionFetch [org.springframework.data.jpa.domain.sample.User(user).colleagues]
 |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).createdAt]
 |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).dateOfBirth]
 |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).emailAddress]
 |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).firstname]
 |  +-BasicFetch [org.springframework.data.jpa.domain.sample.User(user).lastname]
 |  +-EntityDelayedFetchImpl [org.springframework.data.jpa.domain.sample.User(user).manager]
 |  \-DelayedCollectionFetch [org.springframework.data.jpa.domain.sample.User(user).roles]

I'm not sure I can see the hints being applied, but this may simply be an issue with logging.

@MahmoudFawzyKhalil
Copy link
Author

I had done the same in my project by creating a custom RepositoryFactoryBean and replacing a few components, and the fetching works like it for all(), so it works just to add that clause.

I think the issue is with the logging.

gregturn added a commit that referenced this issue Feb 27, 2023
…icate.

When using Querydsl predicates to build a fluent query, be sure to apply the projection hint that generates a fetch graph.

Resolves #2820.
@gregturn gregturn added type: bug A general bug in: repository Repositories abstraction and removed status: waiting-for-triage An issue we've not yet triaged labels Feb 27, 2023
@gregturn gregturn added this to the 3.1 M3 (2023.0.0) milestone Feb 27, 2023
gregturn added a commit that referenced this issue Feb 27, 2023
When using Querydsl predicates to build a fluent query, be sure to apply the projection hint that generates a fetch graph.

Resolves #2820.
mp911de pushed a commit that referenced this issue Mar 3, 2023
When using Querydsl predicates to build a fluent query, be sure to apply the projection hint that generates a fetch graph.

Resolves #2820.
Original pull request: #2827.
@mp911de mp911de closed this as completed in 752fe46 Mar 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: repository Repositories abstraction type: bug A general bug
Projects
None yet
5 participants