You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Data分页异常:页码>1时返回重复数据问题排查

Problem with Spring Data JPA Pagination: Page ≥2 Returns Same Content as Page 1

I've implemented a GET endpoint to return paginated Trip data with optional search criteria:

@GetMapping("/trip")
Page<Trip> all(@SearchSpec Specification<Trip> specs, 
               @RequestParam(defaultValue = "0") int page, 
               @RequestParam(defaultValue = "1000") int pageSize) {
    return repository.findAll(Specification.where(specs), 
                              PageRequest.of(page, pageSize, Sort.by("searchKey").ascending()));
}

When calling the endpoint, page=0 and page=1 return the correct paginated data. However, when page≥2, the returned content is exactly the same as page=1. Adjusting pageSize doesn't fix this issue, and removing the Specification and sorting also doesn't resolve the problem.


Solution

Alright, let's tackle this pagination issue you're facing—this is a common gotcha with Spring Data JPA, but we can work through it step by step. Since you mentioned removing the Specification and sorting didn't fix the problem, the root cause is likely tied to how Spring Data JPA identifies unique records or how the database handles pagination under the hood. Here are the most probable fixes:

1. Ensure Your Trip Entity Has a Valid Unique Primary Key

Spring Data JPA relies entirely on the entity's primary key (annotated with @Id) to track which records belong to which page. If your Trip class doesn't have a unique, non-null primary key, the pagination mechanism can't reliably distinguish between rows, leading to duplicate page results.

How to fix:
Double-check your Trip entity class and make sure it has a properly defined primary key. For example:

@Entity
public class Trip {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // Auto-incrementing unique ID
    private Long id;

    // Your other fields (searchKey, etc.)
    // Getters and setters
}

2. Add a Secondary Sort on a Unique Field

Even if you're sorting by searchKey, if multiple Trip records share the same searchKey value, the database might not return consistent ordering for pages beyond the first. Without a secondary sort on a unique field (like your primary key), the database can return rows in an arbitrary order when using OFFSET (which is what Spring Data uses for pagination).

How to fix:
Modify your PageRequest to include a secondary sort on your primary key to guarantee consistent ordering:

PageRequest.of(page, pageSize, 
               Sort.by("searchKey").ascending()
                   .and(Sort.by("id").ascending())) // Add this line

3. Check for Custom Repository Overrides

If your TripRepository has a custom implementation of findAll(Specification, Pageable) that overrides the default Spring Data behavior, it might be botching the pagination logic.

How to fix:
Verify your repository interface extends the correct base repositories without custom overrides that break pagination:

public interface TripRepository extends JpaRepository<Trip, Long>, JpaSpecificationExecutor<Trip> {
    // No custom findAll methods that override the default pagination logic
}

4. Database-Specific Pagination Quirks (Try Keyset Pagination)

Offset-based pagination (which Spring Data uses by default) can have issues with large offsets or non-unique sorting, especially in databases like MySQL. If the above fixes don't work, consider switching to keyset pagination (also known as "seek pagination") which is more reliable for large datasets.

How to implement:
Modify your endpoint to accept the last searchKey and primary key from the previous page, then use those to fetch the next set of results:

@GetMapping("/trip")
Page<Trip> all(@SearchSpec Specification<Trip> specs, 
               @RequestParam(defaultValue = "0") int page, 
               @RequestParam(defaultValue = "1000") int pageSize,
               @RequestParam(required = false) String lastSearchKey,
               @RequestParam(required = false) Long lastId) {

    // Add keyset filtering if we have the last page's values
    if (lastSearchKey != null && lastId != null) {
        specs = specs.and((root, query, cb) -> 
            cb.or(
                cb.greaterThan(root.get("searchKey"), lastSearchKey),
                cb.and(
                    cb.equal(root.get("searchKey"), lastSearchKey),
                    cb.greaterThan(root.get("id"), lastId)
                )
            )
        );
    }

    return repository.findAll(Specification.where(specs), 
                              PageRequest.of(page, pageSize, 
                                             Sort.by("searchKey").ascending()
                                                 .and(Sort.by("id").ascending())));
}

内容的提问来源于stack exchange,提问作者DezeJongen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:20:05