本例演示使用可滾動結果集 (ResultSet.TYPE_SCROLL_INSENSITIVE) 實現高效分頁查詢:移動游標到指定頁首行,僅讀取當前頁數據,避免全量加載。
核心代碼
public class JDBCScrollConn {
private static String url, username, password;
public Connection conn() { ... } // 同前例
// 簡單查詢
public void query(Connection con, String sql) { ... }
// 分頁查詢核心
public void query(Connection con, String sql, int pageNo, int pageSize) {
try {
if (con == null) throw new Exception("database connection can't use!");
if (sql == null) throw new Exception("check your parameter: 'sql'! don't input null!");
// 1. 創建可滾動只讀 Statement
Statement stmt = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE, // 可滾動,不感知數據變化
ResultSet.CONCUR_READ_ONLY // 只讀
);
ResultSet rs = stmt.executeQuery(sql);
ResultSetMetaData rmeta = rs.getMetaData();
int numColumns = rmeta.getColumnCount();
// 2. 獲取總記錄數(移動到最後一行)
rs.last();
int intRowCount = rs.getRow();
int intPageCount = (intRowCount + pageSize - 1) / pageSize;
// 3. 邊界校正
if (pageNo > intPageCount) pageNo = intPageCount;
// 4. 定位到頁首行並讀取
if (intPageCount > 0) {
rs.absolute((pageNo - 1) * pageSize + 1); // 絕對定位
int i = 0;
while (i < pageSize && !rs.isAfterLast()) {
for (int j = 0; j < numColumns; j++)
System.out.print(rs.getString(j+1) + " ");
i++; rs.next(); System.out.println();
}
}
} catch (Exception e) { System.out.println("query error:" + e); }
}
public void demo(int pageNo, int pageSize) { ... }
public static void main(String[] arg) { ... }
}
ResultSet 類型與併發性
| 常量 | 說明 |
TYPE_FORWARD_ONLY | 默認,僅能 next() 向前,不可滾動 |
TYPE_SCROLL_INSENSITIVE | 可滾動,結果集快照,不見他人提交的修改 |
TYPE_SCROLL_SENSITIVE | 可滾動,實時見他人提交的修改(性能較差) |
CONCUR_READ_ONLY | 只讀,不可更新 |
CONCUR_UPDATABLE | 可更新,支持 updateRow()/deleteRow()/insertRow() |
關鍵游標操作方法
| 方法 | 作用 |
absolute(int row) | 移動到指定行號(1-based,負數從後向前) |
relative(int rows) | 相對當前位置移動 |
first()/last() | 移動到首/尾行 |
beforeFirst()/afterLast() | 移動到首行前/尾行後 |
getRow() | 獲取當前行號(1-based) |
isFirst()/isLast()/isBeforeFirst()/isAfterLast() | 位置判斷 |
性能與局限性分析
- 優勢:避免應用層全量緩存,內存佔用穩定
- 劣勢:
rs.last() 驅動可能需遍歷全表獲取行數;游標佔用數據庫資源;長事務風險
- 數據庫支持差異:Oracle/PostgreSQL/MySQL 支持度不同,MySQL 需配合
useCursorFetch=true
現代分頁最佳實踐
- SQL 級分頁:
LIMIT offset, size (MySQL/PostgreSQL)、OFFSET ... FETCH NEXT (Oracle 12c+、SQL Server 2012+)
- 游標/Keyset 分頁:
WHERE id > lastId ORDER BY id LIMIT size,深分頁性能優越
- 框架整合:MyBatis
RowBounds、PageHelper、Spring Data Pageable
- 計數優化:緩存總數、估算行數、異步計數