Skip to content

Commit f0c7819

Browse files
gregturnmp911de
authored andcommitted
Apply hints for pagined projections on fetchable Querydsl queries.
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.
1 parent dbb92d3 commit f0c7819

File tree

4 files changed

+76
-26
lines changed

4 files changed

+76
-26
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java

+10-6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.jpa.repository.support;
1717

18+
import jakarta.persistence.EntityManager;
19+
1820
import java.util.ArrayList;
1921
import java.util.Collection;
2022
import java.util.Collections;
@@ -23,8 +25,6 @@
2325
import java.util.function.Function;
2426
import java.util.stream.Stream;
2527

26-
import jakarta.persistence.EntityManager;
27-
2828
import org.springframework.dao.IncorrectResultSizeDataAccessException;
2929
import org.springframework.data.domain.Page;
3030
import org.springframework.data.domain.PageImpl;
@@ -69,8 +69,7 @@ public FetchableFluentQueryByPredicate(Predicate predicate, Class<S> entityType,
6969
private FetchableFluentQueryByPredicate(Predicate predicate, Class<S> entityType, Class<R> resultType, Sort sort,
7070
Collection<String> properties, Function<Sort, AbstractJPAQuery<?, ?>> finder,
7171
BiFunction<Sort, Pageable, AbstractJPAQuery<?, ?>> pagedFinder, Function<Predicate, Long> countOperation,
72-
Function<Predicate, Boolean> existsOperation,
73-
EntityManager entityManager) {
72+
Function<Predicate, Boolean> existsOperation, EntityManager entityManager) {
7473

7574
super(resultType, sort, properties, entityType);
7675
this.predicate = predicate;
@@ -175,8 +174,13 @@ public boolean exists() {
175174

176175
private Page<R> readPage(Pageable pageable) {
177176

178-
AbstractJPAQuery<?, ?> pagedQuery = pagedFinder.apply(sort, pageable);
179-
List<R> paginatedResults = convert(pagedQuery.fetch());
177+
AbstractJPAQuery<?, ?> query = pagedFinder.apply(sort, pageable);
178+
179+
if (!properties.isEmpty()) {
180+
query.setHint(EntityGraphFactory.HINT, EntityGraphFactory.create(entityManager, entityType, properties));
181+
}
182+
183+
List<R> paginatedResults = convert(query.fetch());
180184

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

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java

+46-5
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,13 @@
1515
*/
1616
package org.springframework.data.jpa.repository;
1717

18-
import static java.util.Arrays.asList;
18+
import static java.util.Arrays.*;
1919
import static org.assertj.core.api.Assertions.*;
20-
import static org.springframework.data.domain.Example.of;
20+
import static org.springframework.data.domain.Example.*;
2121
import static org.springframework.data.domain.ExampleMatcher.*;
22-
import static org.springframework.data.domain.Sort.Direction.ASC;
23-
import static org.springframework.data.domain.Sort.Direction.DESC;
22+
import static org.springframework.data.domain.Sort.Direction.*;
23+
import static org.springframework.data.jpa.domain.Specification.*;
2424
import static org.springframework.data.jpa.domain.Specification.not;
25-
import static org.springframework.data.jpa.domain.Specification.where;
2625
import static org.springframework.data.jpa.domain.sample.UserSpecifications.*;
2726

2827
import jakarta.persistence.EntityManager;
@@ -53,6 +52,7 @@
5352
import org.springframework.data.domain.Sort.Order;
5453
import org.springframework.data.jpa.domain.Specification;
5554
import org.springframework.data.jpa.domain.sample.Address;
55+
import org.springframework.data.jpa.domain.sample.QUser;
5656
import org.springframework.data.jpa.domain.sample.Role;
5757
import org.springframework.data.jpa.domain.sample.SpecialUser;
5858
import org.springframework.data.jpa.domain.sample.User;
@@ -2421,6 +2421,47 @@ void findByFluentSpecificationWithSimplePropertyPathsDoesntLoadUnrequestedPaths(
24212421
);
24222422
}
24232423

2424+
@Test // GH-2820
2425+
void findByFluentPredicateWithProjectionAndPageRequest() {
2426+
2427+
flushTestUsers();
2428+
em.clear();
2429+
2430+
Page<User> users = repository.findBy(QUser.user.firstname.contains("v"), q -> q //
2431+
.project("firstname") //
2432+
.page(PageRequest.of(0, 10)));
2433+
2434+
assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(),
2435+
thirdUser.getFirstname(), fourthUser.getFirstname());
2436+
}
2437+
2438+
@Test // GH-2820
2439+
void findByFluentPredicateWithProjectionAndAll() {
2440+
2441+
flushTestUsers();
2442+
em.clear();
2443+
2444+
List<User> users = repository.findBy(QUser.user.firstname.contains("v"), q -> q //
2445+
.project("firstname") //
2446+
.all());
2447+
2448+
assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(),
2449+
thirdUser.getFirstname(), fourthUser.getFirstname());
2450+
}
2451+
2452+
@Test // GH-2820
2453+
void findByFluentPredicateWithPageRequest() {
2454+
2455+
flushTestUsers();
2456+
em.clear();
2457+
2458+
Page<User> users = repository.findBy(QUser.user.firstname.contains("v"), q -> q //
2459+
.page(PageRequest.of(0, 10)));
2460+
2461+
assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(),
2462+
thirdUser.getFirstname(), fourthUser.getFirstname());
2463+
}
2464+
24242465
@Test // GH-2274
24252466
void findByFluentSpecificationWithCollectionPropertyPathsDoesntLoadUnrequestedPaths() {
24262467

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java

+19-5
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,29 @@
1818
import jakarta.persistence.EntityManager;
1919
import jakarta.persistence.QueryHint;
2020

21-
import java.util.*;
21+
import java.util.Collection;
22+
import java.util.Date;
23+
import java.util.List;
24+
import java.util.Map;
25+
import java.util.Optional;
26+
import java.util.Set;
2227
import java.util.stream.Stream;
2328

24-
import org.springframework.data.domain.*;
29+
import org.springframework.data.domain.Page;
30+
import org.springframework.data.domain.PageRequest;
31+
import org.springframework.data.domain.Pageable;
32+
import org.springframework.data.domain.Slice;
33+
import org.springframework.data.domain.Sort;
2534
import org.springframework.data.jpa.domain.sample.Role;
2635
import org.springframework.data.jpa.domain.sample.SpecialUser;
2736
import org.springframework.data.jpa.domain.sample.User;
28-
import org.springframework.data.jpa.repository.*;
37+
import org.springframework.data.jpa.repository.JpaRepository;
38+
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
39+
import org.springframework.data.jpa.repository.Modifying;
40+
import org.springframework.data.jpa.repository.Query;
41+
import org.springframework.data.jpa.repository.QueryHints;
2942
import org.springframework.data.jpa.repository.query.Procedure;
43+
import org.springframework.data.querydsl.ListQuerydslPredicateExecutor;
3044
import org.springframework.data.repository.CrudRepository;
3145
import org.springframework.data.repository.query.Param;
3246
import org.springframework.transaction.annotation.Transactional;
@@ -45,8 +59,8 @@
4559
* @author Diego Krupitza
4660
* @author Geoffrey Deremetz
4761
*/
48-
public interface UserRepository
49-
extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User>, UserRepositoryCustom {
62+
public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User>,
63+
UserRepositoryCustom, ListQuerydslPredicateExecutor<User> {
5064

5165
/**
5266
* Retrieve users by their lastname. The finder {@literal User.findByLastname} is declared in

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java

+1-10
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@
3232
import org.springframework.data.querydsl.SimpleEntityPathResolver;
3333
import org.springframework.test.util.ReflectionTestUtils;
3434

35-
import com.querydsl.core.types.EntityPath;
36-
3735
/**
3836
* Unit tests for {@link EntityPathResolver} related tests on {@link JpaRepositoryFactoryBean}.
3937
*
@@ -47,14 +45,7 @@ class JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests {
4745
@EnableJpaRepositories(basePackageClasses = UserRepository.class, //
4846
includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = UserRepository.class))
4947
static class BaseConfig {
50-
51-
static final EntityPathResolver RESOLVER = new EntityPathResolver() {
52-
53-
@Override
54-
public <T> EntityPath<T> createPath(Class<T> domainClass) {
55-
return null;
56-
}
57-
};
48+
static final EntityPathResolver RESOLVER = SimpleEntityPathResolver.INSTANCE;
5849
}
5950

6051
@Configuration

0 commit comments

Comments
 (0)