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.
This commit is contained in:
committed by
Mark Paluch
parent
aab0efa5df
commit
752fe463ed
@@ -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<S, R> extends FluentQuerySupport<S, R> imp
|
||||
private FetchableFluentQueryByPredicate(Predicate predicate, Class<S> entityType, Class<R> resultType, Sort sort,
|
||||
Collection<String> properties, Function<Sort, AbstractJPAQuery<?, ?>> finder,
|
||||
BiFunction<Sort, Pageable, AbstractJPAQuery<?, ?>> pagedFinder, Function<Predicate, Long> countOperation,
|
||||
Function<Predicate, Boolean> existsOperation,
|
||||
EntityManager entityManager) {
|
||||
Function<Predicate, Boolean> existsOperation, EntityManager entityManager) {
|
||||
|
||||
super(resultType, sort, properties, entityType);
|
||||
this.predicate = predicate;
|
||||
@@ -175,8 +174,13 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<S, R> imp
|
||||
|
||||
private Page<R> readPage(Pageable pageable) {
|
||||
|
||||
AbstractJPAQuery<?, ?> pagedQuery = pagedFinder.apply(sort, pageable);
|
||||
List<R> paginatedResults = convert(pagedQuery.fetch());
|
||||
AbstractJPAQuery<?, ?> query = pagedFinder.apply(sort, pageable);
|
||||
|
||||
if (!properties.isEmpty()) {
|
||||
query.setHint(EntityGraphFactory.HINT, EntityGraphFactory.create(entityManager, entityType, properties));
|
||||
}
|
||||
|
||||
List<R> paginatedResults = convert(query.fetch());
|
||||
|
||||
return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> countOperation.apply(predicate));
|
||||
}
|
||||
|
||||
@@ -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<User> 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<User> 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<User> 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() {
|
||||
|
||||
|
||||
@@ -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<User, Integer>, JpaSpecificationExecutor<User>, UserRepositoryCustom {
|
||||
public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User>,
|
||||
UserRepositoryCustom, ListQuerydslPredicateExecutor<User> {
|
||||
|
||||
/**
|
||||
* Retrieve users by their lastname. The finder {@literal User.findByLastname} is declared in
|
||||
|
||||
@@ -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 <T> EntityPath<T> createPath(Class<T> domainClass) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
static final EntityPathResolver RESOLVER = SimpleEntityPathResolver.INSTANCE;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
Reference in New Issue
Block a user