Skip to content

Latest commit

 

History

History
58 lines (39 loc) · 2.91 KB

File metadata and controls

58 lines (39 loc) · 2.91 KB

minimart-api

Minimal reproducer: sortBy(…) is ignored by fluent Specification queries (JpaSpecificationExecutor.findBy(…)) in two situations:

  1. slice(…) with a PageRequest that carries no sort
  2. page(…) / slice(…) with Pageable.unpaged()

In both cases the executed SQL has no ORDER BY at all, while page(…) with the same unsorted PageRequest applies the sort correctly.

  • Spring Boot 4.1.0 / Spring Data JPA 4.1.0 / Java 21
  • Embedded in-memory H2 — no external setup required (the behavior is database-independent; it reproduces on PostgreSQL 17 as well)

Running the reproducer

./mvnw spring-boot:run        # Windows: .\mvnw.cmd spring-boot:run

The application executes both cases on startup, prints the results, and exits.

The demo seeds 15 PAID orders whose createdAt values do not follow insertion order, then runs both cases. org.hibernate.SQL=debug is enabled, so the missing ORDER BY is directly visible in the log.

Case 1 — slice() with an unsorted PageRequest

Slice<CustomerOrder> slice = orders.findBy(
        OrderSpecs.statusIs(Status.PAID),
        q -> q.sortBy(Sort.by(DESC, "createdAt")).slice(PageRequest.of(0, 5)));
result executed SQL
expected ORD-013, ORD-007, … (createdAt desc) … order by co1_0.created_at desc offset ? rows fetch first ? rows only
actual ORD-001 … ORD-005 (insertion order) … offset ? rows fetch first ? rows only — no order by

page(…) with the exact same arguments sorts correctly, so switching pagination from Page to Slice (e.g. for infinite scrolling) silently changes the row order and causes duplicated/missing items between scroll requests.

Case 2 — unpaged page() / slice()

Page<CustomerOrder> page = orders.findBy(
        OrderSpecs.amountAtLeast(20_000),
        q -> q.sortBy(Sort.by(DESC, "totalAmount")).page(Pageable.unpaged()));
result
expected ORD-015, ORD-014, … (totalAmount desc)
actual ORD-011, ORD-012, … (insertion order) — the SQL has no order by

Root cause (Spring Data JPA sources)

FetchableFluentQueryBySpecification uses pageable.getSort() instead of pageable.getSortOr(this.sort):

  • readSlice(…) builds its query with createSortedAndProjectedQuery(pageable.getSort()), dropping the sortBy(…) sort (case 1), while readPage(…) right below correctly uses getSortOr(this.sort).
  • The unpaged branches of slice(Pageable), page(Pageable), and page(Pageable, countSpec) call all(pageable.getSort()) (case 2). The getSortOr(this.sort) calls introduced by the GH-3762 fix (commit 16495066f) are no longer present on main.

The Querydsl twin, FetchableFluentQueryByPredicate, uses pageable.getSortOr(this.sort) in all of these paths and behaves as expected (verified by QuerydslJpaPredicateExecutorUnitTests.findByFluentPredicateSlice, GH-3764).