diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java index 61268e608..b70b3b4d4 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/FetchableFluentQueryByPredicate.java @@ -15,6 +15,8 @@ */ package org.springframework.data.jpa.repository.support; +import jakarta.persistence.EntityManager; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -23,8 +25,6 @@ import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Stream; -import jakarta.persistence.EntityManager; - import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -69,8 +69,7 @@ class FetchableFluentQueryByPredicate extends FluentQuerySupport imp private FetchableFluentQueryByPredicate(Predicate predicate, Class entityType, Class resultType, Sort sort, Collection properties, Function> finder, BiFunction> pagedFinder, Function countOperation, - Function existsOperation, - EntityManager entityManager) { + Function existsOperation, EntityManager entityManager) { super(resultType, sort, properties, entityType); this.predicate = predicate; @@ -175,8 +174,13 @@ class FetchableFluentQueryByPredicate extends FluentQuerySupport imp private Page readPage(Pageable pageable) { - AbstractJPAQuery pagedQuery = pagedFinder.apply(sort, pageable); - List paginatedResults = convert(pagedQuery.fetch()); + AbstractJPAQuery query = pagedFinder.apply(sort, pageable); + + if (!properties.isEmpty()) { + query.setHint(EntityGraphFactory.HINT, EntityGraphFactory.create(entityManager, entityType, properties)); + } + + List paginatedResults = convert(query.fetch()); return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> countOperation.apply(predicate)); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index f555d1baa..b450692a7 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -15,18 +15,17 @@ */ package org.springframework.data.jpa.repository; -import static java.util.Arrays.asList; +import static java.util.Arrays.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.springframework.data.domain.Example.of; +import static org.springframework.data.domain.Example.*; import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher; import static org.springframework.data.domain.ExampleMatcher.StringMatcher; import static org.springframework.data.domain.ExampleMatcher.matching; -import static org.springframework.data.domain.Sort.Direction.ASC; -import static org.springframework.data.domain.Sort.Direction.DESC; +import static org.springframework.data.domain.Sort.Direction.*; +import static org.springframework.data.jpa.domain.Specification.*; import static org.springframework.data.jpa.domain.Specification.not; -import static org.springframework.data.jpa.domain.Specification.where; import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasAgeLess; import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasFirstname; import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasFirstnameLike; @@ -75,6 +74,7 @@ import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.sample.Address; +import org.springframework.data.jpa.domain.sample.QUser; import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.domain.sample.SpecialUser; import org.springframework.data.jpa.domain.sample.User; @@ -2444,6 +2444,47 @@ class UserRepositoryTests { ); } + @Test // GH-2820 + void findByFluentPredicateWithProjectionAndPageRequest() { + + flushTestUsers(); + em.clear(); + + Page users = repository.findBy(QUser.user.firstname.contains("v"), q -> q // + .project("firstname") // + .page(PageRequest.of(0, 10))); + + assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(), + thirdUser.getFirstname(), fourthUser.getFirstname()); + } + + @Test // GH-2820 + void findByFluentPredicateWithProjectionAndAll() { + + flushTestUsers(); + em.clear(); + + List users = repository.findBy(QUser.user.firstname.contains("v"), q -> q // + .project("firstname") // + .all()); + + assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(), + thirdUser.getFirstname(), fourthUser.getFirstname()); + } + + @Test // GH-2820 + void findByFluentPredicateWithPageRequest() { + + flushTestUsers(); + em.clear(); + + Page users = repository.findBy(QUser.user.firstname.contains("v"), q -> q // + .page(PageRequest.of(0, 10))); + + assertThat(users).extracting(User::getFirstname).containsExactlyInAnyOrder(firstUser.getFirstname(), + thirdUser.getFirstname(), fourthUser.getFirstname()); + } + @Test // GH-2274 void findByFluentSpecificationWithCollectionPropertyPathsDoesntLoadUnrequestedPaths() { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java index 6ff229d87..ed5fcdbac 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java @@ -18,15 +18,29 @@ package org.springframework.data.jpa.repository.sample; import jakarta.persistence.EntityManager; import jakarta.persistence.QueryHint; -import java.util.*; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.stream.Stream; -import org.springframework.data.domain.*; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.sample.Role; import org.springframework.data.jpa.domain.sample.SpecialUser; import org.springframework.data.jpa.domain.sample.User; -import org.springframework.data.jpa.repository.*; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.QueryHints; import org.springframework.data.jpa.repository.query.Procedure; +import org.springframework.data.querydsl.ListQuerydslPredicateExecutor; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; @@ -45,8 +59,8 @@ import org.springframework.transaction.annotation.Transactional; * @author Diego Krupitza * @author Geoffrey Deremetz */ -public interface UserRepository - extends JpaRepository, JpaSpecificationExecutor, UserRepositoryCustom { +public interface UserRepository extends JpaRepository, JpaSpecificationExecutor, + UserRepositoryCustom, ListQuerydslPredicateExecutor { /** * Retrieve users by their lastname. The finder {@literal User.findByLastname} is declared in diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java index 345a71e15..76e83da05 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests.java @@ -32,8 +32,6 @@ import org.springframework.data.querydsl.EntityPathResolver; import org.springframework.data.querydsl.SimpleEntityPathResolver; import org.springframework.test.util.ReflectionTestUtils; -import com.querydsl.core.types.EntityPath; - /** * Unit tests for {@link EntityPathResolver} related tests on {@link JpaRepositoryFactoryBean}. * @@ -47,14 +45,7 @@ class JpaRepositoryFactoryBeanEntityPathResolverIntegrationTests { @EnableJpaRepositories(basePackageClasses = UserRepository.class, // includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = UserRepository.class)) static class BaseConfig { - - static final EntityPathResolver RESOLVER = new EntityPathResolver() { - - @Override - public EntityPath createPath(Class domainClass) { - return null; - } - }; + static final EntityPathResolver RESOLVER = SimpleEntityPathResolver.INSTANCE; } @Configuration