Skip to content

Commit 752fe46

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 aab0efa commit 752fe46

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,18 +15,17 @@
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.assertThat;
2020
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
2121
import static org.assertj.core.api.Assertions.assertThatThrownBy;
22-
import static org.springframework.data.domain.Example.of;
22+
import static org.springframework.data.domain.Example.*;
2323
import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher;
2424
import static org.springframework.data.domain.ExampleMatcher.StringMatcher;
2525
import static org.springframework.data.domain.ExampleMatcher.matching;
26-
import static org.springframework.data.domain.Sort.Direction.ASC;
27-
import static org.springframework.data.domain.Sort.Direction.DESC;
26+
import static org.springframework.data.domain.Sort.Direction.*;
27+
import static org.springframework.data.jpa.domain.Specification.*;
2828
import static org.springframework.data.jpa.domain.Specification.not;
29-
import static org.springframework.data.jpa.domain.Specification.where;
3029
import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasAgeLess;
3130
import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasFirstname;
3231
import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasFirstnameLike;
@@ -75,6 +74,7 @@
7574
import org.springframework.data.domain.Sort.Order;
7675
import org.springframework.data.jpa.domain.Specification;
7776
import org.springframework.data.jpa.domain.sample.Address;
77+
import org.springframework.data.jpa.domain.sample.QUser;
7878
import org.springframework.data.jpa.domain.sample.Role;
7979
import org.springframework.data.jpa.domain.sample.SpecialUser;
8080
import org.springframework.data.jpa.domain.sample.User;
@@ -2444,6 +2444,47 @@ void findByFluentSpecificationWithSimplePropertyPathsDoesntLoadUnrequestedPaths(
24442444
);
24452445
}
24462446

2447+
@Test // GH-2820
2448+
void findByFluentPredicateWithProjectionAndPageRequest() {
2449+
2450+
flushTestUsers();
2451+
em.clear();
2452+
2453+
Page<User> users = repository.findBy(QUser.user.firstname.contains("v"), q -> q //
2454+
.project("firstname") //
2455+
.page(PageRequest.of(0, 10)));
2456+
2457+
assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(),
2458+
thirdUser.getFirstname(), fourthUser.getFirstname());
2459+
}
2460+
2461+
@Test // GH-2820
2462+
void findByFluentPredicateWithProjectionAndAll() {
2463+
2464+
flushTestUsers();
2465+
em.clear();
2466+
2467+
List<User> users = repository.findBy(QUser.user.firstname.contains("v"), q -> q //
2468+
.project("firstname") //
2469+
.all());
2470+
2471+
assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(),
2472+
thirdUser.getFirstname(), fourthUser.getFirstname());
2473+
}
2474+
2475+
@Test // GH-2820
2476+
void findByFluentPredicateWithPageRequest() {
2477+
2478+
flushTestUsers();
2479+
em.clear();
2480+
2481+
Page<User> users = repository.findBy(QUser.user.firstname.contains("v"), q -> q //
2482+
.page(PageRequest.of(0, 10)));
2483+
2484+
assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(),
2485+
thirdUser.getFirstname(), fourthUser.getFirstname());
2486+
}
2487+
24472488
@Test // GH-2274
24482489
void findByFluentSpecificationWithCollectionPropertyPathsDoesntLoadUnrequestedPaths() {
24492490

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)