Consider Sort override from Pageable using Fluent Query API.
We now consider properly a sort value from a given Pageable. Previously, the sort Parameter was not applied from Pageable but from a previous sort(…) call. Also, we apply the given Sort to the resulting Pageable to ensure sort continuity. Closes #3762
This commit is contained in:
@@ -132,7 +132,7 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<S, R> imp
|
||||
@Override
|
||||
public R oneValue() {
|
||||
|
||||
List<?> results = createSortedAndProjectedQuery() //
|
||||
List<?> results = createSortedAndProjectedQuery(this.sort) //
|
||||
.limit(2) // Never need more than 2 values
|
||||
.fetch();
|
||||
|
||||
@@ -146,7 +146,7 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<S, R> imp
|
||||
@Override
|
||||
public R firstValue() {
|
||||
|
||||
List<?> results = createSortedAndProjectedQuery() //
|
||||
List<?> results = createSortedAndProjectedQuery(this.sort) //
|
||||
.limit(1) // Never need more than 1 value
|
||||
.fetch();
|
||||
|
||||
@@ -155,7 +155,11 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<S, R> imp
|
||||
|
||||
@Override
|
||||
public List<R> all() {
|
||||
return convert(createSortedAndProjectedQuery().fetch());
|
||||
return all(this.sort);
|
||||
}
|
||||
|
||||
private List<R> all(Sort sort) {
|
||||
return convert(createSortedAndProjectedQuery(sort).fetch());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -168,13 +172,13 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<S, R> imp
|
||||
|
||||
@Override
|
||||
public Page<R> page(Pageable pageable) {
|
||||
return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable);
|
||||
return pageable.isUnpaged() ? new PageImpl<>(all(pageable.getSortOr(this.sort))) : readPage(pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<R> stream() {
|
||||
|
||||
return createSortedAndProjectedQuery() //
|
||||
return createSortedAndProjectedQuery(this.sort) //
|
||||
.stream() //
|
||||
.map(getConversionFunction());
|
||||
}
|
||||
@@ -189,7 +193,7 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<S, R> imp
|
||||
return existsOperation.apply(predicate);
|
||||
}
|
||||
|
||||
private AbstractJPAQuery<?, ?> createSortedAndProjectedQuery() {
|
||||
private AbstractJPAQuery<?, ?> createSortedAndProjectedQuery(Sort sort) {
|
||||
|
||||
AbstractJPAQuery<?, ?> query = finder.apply(sort);
|
||||
|
||||
@@ -206,6 +210,7 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<S, R> imp
|
||||
|
||||
private Page<R> readPage(Pageable pageable) {
|
||||
|
||||
Sort sort = pageable.getSortOr(this.sort);
|
||||
AbstractJPAQuery<?, ?> query = pagedFinder.apply(sort, pageable);
|
||||
|
||||
if (!properties.isEmpty()) {
|
||||
@@ -214,7 +219,8 @@ class FetchableFluentQueryByPredicate<S, R> extends FluentQuerySupport<S, R> imp
|
||||
|
||||
List<R> paginatedResults = convert(query.fetch());
|
||||
|
||||
return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> countOperation.apply(predicate));
|
||||
return PageableExecutionUtils.getPage(paginatedResults, withSort(pageable, sort),
|
||||
() -> countOperation.apply(predicate));
|
||||
}
|
||||
|
||||
private List<R> convert(List<?> resultList) {
|
||||
|
||||
@@ -89,8 +89,14 @@ class FetchableFluentQueryBySpecification<S, R> extends FluentQuerySupport<S, R>
|
||||
|
||||
Assert.notNull(sort, "Sort must not be null");
|
||||
|
||||
return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, this.sort.and(sort), limit,
|
||||
properties, finder, scroll, countOperation, existsOperation, entityManager, projectionFactory);
|
||||
Sort sort1 = this.sort.and(sort);
|
||||
|
||||
if (this.sort == sort1) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, sort1, limit, properties, finder,
|
||||
scroll, countOperation, existsOperation, entityManager, projectionFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -98,8 +104,8 @@ class FetchableFluentQueryBySpecification<S, R> extends FluentQuerySupport<S, R>
|
||||
|
||||
Assert.isTrue(limit >= 0, "Limit must not be negative");
|
||||
|
||||
return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, sort, limit,
|
||||
properties, finder, scroll, countOperation, existsOperation, entityManager, projectionFactory);
|
||||
return new FetchableFluentQueryBySpecification<>(spec, entityType, resultType, sort, limit, properties, finder,
|
||||
scroll, countOperation, existsOperation, entityManager, projectionFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -124,7 +130,7 @@ class FetchableFluentQueryBySpecification<S, R> extends FluentQuerySupport<S, R>
|
||||
@Override
|
||||
public R oneValue() {
|
||||
|
||||
List<?> results = createSortedAndProjectedQuery() //
|
||||
List<?> results = createSortedAndProjectedQuery(this.sort) //
|
||||
.setMaxResults(2) // Never need more than 2 values
|
||||
.getResultList();
|
||||
|
||||
@@ -138,7 +144,7 @@ class FetchableFluentQueryBySpecification<S, R> extends FluentQuerySupport<S, R>
|
||||
@Override
|
||||
public R firstValue() {
|
||||
|
||||
List<?> results = createSortedAndProjectedQuery() //
|
||||
List<?> results = createSortedAndProjectedQuery(this.sort) //
|
||||
.setMaxResults(1) // Never need more than 1 value
|
||||
.getResultList();
|
||||
|
||||
@@ -147,7 +153,11 @@ class FetchableFluentQueryBySpecification<S, R> extends FluentQuerySupport<S, R>
|
||||
|
||||
@Override
|
||||
public List<R> all() {
|
||||
return convert(createSortedAndProjectedQuery().getResultList());
|
||||
return all(this.sort);
|
||||
}
|
||||
|
||||
private List<R> all(Sort sort) {
|
||||
return convert(createSortedAndProjectedQuery(sort).getResultList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -160,13 +170,13 @@ class FetchableFluentQueryBySpecification<S, R> extends FluentQuerySupport<S, R>
|
||||
|
||||
@Override
|
||||
public Page<R> page(Pageable pageable) {
|
||||
return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable);
|
||||
return pageable.isUnpaged() ? new PageImpl<>(all(pageable.getSortOr(this.sort))) : readPage(pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<R> stream() {
|
||||
|
||||
return createSortedAndProjectedQuery() //
|
||||
return createSortedAndProjectedQuery(this.sort) //
|
||||
.getResultStream() //
|
||||
.map(getConversionFunction());
|
||||
}
|
||||
@@ -181,7 +191,7 @@ class FetchableFluentQueryBySpecification<S, R> extends FluentQuerySupport<S, R>
|
||||
return existsOperation.apply(spec);
|
||||
}
|
||||
|
||||
private TypedQuery<S> createSortedAndProjectedQuery() {
|
||||
private TypedQuery<S> createSortedAndProjectedQuery(Sort sort) {
|
||||
|
||||
TypedQuery<S> query = finder.apply(sort);
|
||||
|
||||
@@ -198,7 +208,8 @@ class FetchableFluentQueryBySpecification<S, R> extends FluentQuerySupport<S, R>
|
||||
|
||||
private Page<R> readPage(Pageable pageable) {
|
||||
|
||||
TypedQuery<S> pagedQuery = createSortedAndProjectedQuery();
|
||||
Sort sort = pageable.getSortOr(this.sort);
|
||||
TypedQuery<S> pagedQuery = createSortedAndProjectedQuery(sort);
|
||||
|
||||
if (pageable.isPaged()) {
|
||||
pagedQuery.setFirstResult(PageableUtils.getOffsetAsInteger(pageable));
|
||||
@@ -207,7 +218,7 @@ class FetchableFluentQueryBySpecification<S, R> extends FluentQuerySupport<S, R>
|
||||
|
||||
List<R> paginatedResults = convert(pagedQuery.getResultList());
|
||||
|
||||
return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> countOperation.apply(spec));
|
||||
return PageableExecutionUtils.getPage(paginatedResults, withSort(pageable, sort), () -> countOperation.apply(spec));
|
||||
}
|
||||
|
||||
private List<R> convert(List<S> resultList) {
|
||||
|
||||
@@ -24,6 +24,8 @@ import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.ScrollPosition;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
@@ -49,7 +51,7 @@ abstract class FluentQuerySupport<S, R> {
|
||||
protected final ProjectionFactory projectionFactory;
|
||||
|
||||
FluentQuerySupport(Class<R> resultType, Sort sort, int limit, @Nullable Collection<String> properties,
|
||||
Class<S> entityType, ProjectionFactory projectionFactory) {
|
||||
Class<S> entityType, ProjectionFactory projectionFactory) {
|
||||
|
||||
this.resultType = resultType;
|
||||
this.sort = sort;
|
||||
@@ -87,6 +89,15 @@ abstract class FluentQuerySupport<S, R> {
|
||||
return o -> DefaultConversionService.getSharedInstance().convert(o, targetType);
|
||||
}
|
||||
|
||||
Pageable withSort(Pageable pageable, Sort sort) {
|
||||
|
||||
if (pageable instanceof PageRequest pr && pageable.getSort() != sort) {
|
||||
return pr.withSort(sort);
|
||||
}
|
||||
|
||||
return pageable;
|
||||
}
|
||||
|
||||
interface ScrollQueryFactory {
|
||||
Query createQuery(Sort sort, ScrollPosition scrollPosition);
|
||||
}
|
||||
|
||||
@@ -2434,6 +2434,24 @@ class UserRepositoryTests {
|
||||
assertThat(page1.getContent()).containsExactly(fourthUser);
|
||||
}
|
||||
|
||||
@Test // GH-3762
|
||||
void findByFluentExamplePageSortOverride() {
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
User prototype = new User();
|
||||
prototype.setFirstname("v");
|
||||
|
||||
Example<User> userProbe = of(prototype, matching().withIgnorePaths("age", "createdAt", "active")
|
||||
.withMatcher("firstname", GenericPropertyMatcher::contains));
|
||||
|
||||
Page<User> page = repository.findBy(userProbe, //
|
||||
q -> q.sortBy(Sort.by("firstname")).page(PageRequest.of(0, 2, Sort.by(DESC, "firstname"))));
|
||||
|
||||
assertThat(page.getContent()).containsExactly(fourthUser, firstUser);
|
||||
assertThat(repository.findAll(page.nextPageable())).containsExactly(secondUser, thirdUser);
|
||||
}
|
||||
|
||||
@Test // GH-2294
|
||||
void findByFluentExampleWithInterfaceBasedProjection() {
|
||||
|
||||
@@ -2689,6 +2707,18 @@ class UserRepositoryTests {
|
||||
assertThat(page1.getContent()).containsExactly(fourthUser);
|
||||
}
|
||||
|
||||
@Test // GH-3762
|
||||
void findByFluentSpecificationSortOverridePage() {
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
Page<User> page = repository.findBy(userHasFirstnameLike("v"),
|
||||
q -> q.sortBy(Sort.by("firstname")).page(PageRequest.of(0, 2, Sort.by(DESC, "firstname"))));
|
||||
|
||||
assertThat(page.getContent()).containsExactly(fourthUser, firstUser);
|
||||
assertThat(repository.findAll(page.nextPageable())).containsExactly(secondUser, thirdUser);
|
||||
}
|
||||
|
||||
@Test // GH-2274
|
||||
void findByFluentSpecificationWithInterfaceBasedProjection() {
|
||||
|
||||
|
||||
@@ -399,6 +399,19 @@ class QuerydslJpaPredicateExecutorUnitTests {
|
||||
assertThat(page1.getContent()).containsExactly(oliver);
|
||||
}
|
||||
|
||||
@Test // GH-3762
|
||||
void findByFluentPredicateSortOverridePage() {
|
||||
|
||||
Predicate predicate = user.firstname.contains("v");
|
||||
|
||||
Page<User> page = predicateExecutor.findBy(predicate,
|
||||
q -> q.sortBy(Sort.by("firstname")).page(PageRequest.of(0, 1, Sort.by(Direction.DESC, "firstname"))));
|
||||
|
||||
assertThat(page.getContent()).containsOnly(oliver);
|
||||
assertThat(predicateExecutor.findAll(predicate, page.nextPageable())).containsOnly(dave);
|
||||
|
||||
}
|
||||
|
||||
@Test // GH-2294
|
||||
void findByFluentPredicateWithInterfaceBasedProjection() {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user