分頁是業務系統核心需求。本文實現一個通用的內存分頁器(假分頁),一次性查出全量數據再切片,適配所有 JDBC 數據庫,附帶適用場景與性能邊界分析。
Pager<T>import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Pager<T> {
private final int pageSize;
private final List<T> data;
private Pager(List<T> data, int pageSize) {
if (data == null || data.isEmpty()) {
throw new IllegalArgumentException("data must be not empty!");
}
this.data = data;
this.pageSize = pageSize;
}
public static <T> Pager<T> create(List<T> data, int pageSize) {
return new Pager<>(data, pageSize);
}
/** 獲取第 pageNum 頁數據(1-based) */
public List<T> getPagedList(int pageNum) {
int fromIndex = (pageNum - 1) * pageSize;
if (fromIndex >= data.size()) {
return Collections.emptyList();
}
int toIndex = Math.min(pageNum * pageSize, data.size());
return data.subList(fromIndex, toIndex);
}
public int getPageSize() { return pageSize; }
public List<T> getData() { return data; }
public int getTotalCount() { return data.size(); }
public int getTotalPages() { return (data.size() + pageSize - 1) / pageSize; }
}
List<Integer> allData = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12);
Pager<Integer> pager = Pager.create(allData, 10);
System.out.println(pager.getPagedList(1)); // [1..10]
System.out.println(pager.getPagedList(2)); // [11, 12]
System.out.println(pager.getPagedList(3)); // []
| 維度 | 說明 |
|---|---|
| 優點 | ① 兼容所有 JDBC 數據庫,無需寫分頁 SQL 方言 ② 代碼極簡,零依賴,易維護 ③ 適合數據量固定、較小(<1萬條)的字典表、配置表、後台管理列表 |
| 缺點 | ① 全量查詢佔用大量內存,大數據量導致 OOM ② 網絡傳輸全量數據,延遲高、帶佔寬 ③ 無法利用數據庫索引優化,查詢慢 ④ 不支持實時數據(查詢快照) |
| 適用場景 | 后台管理系統的基礎數據字典、配置項、用戶列表(<5000條)、導出 Excel 前的全量獲取 |
| 禁用場景 | 日誌表、訂單表、用戶行為表等增長型大表;高併發實時查詢接口 |
ResultSet 游標模式(setFetchSize),邊讀邊發,但失去「任意跳頁」能力。LIMIT/OFFSET 或 ROW_NUMBER() 真分頁。內存分頁是「以空間換時間、以簡單換通用」的權衡方案。用對場景是神器,用錯場景是炸彈。建議在 Service 層封裝 PageService,內部根據數據量自動路由到「內存分頁」或「SQL 真分頁」,對上層 Controller 透明輸出統一 PageResult<T>,實現分頁策略的無感知切換。