直接使用 JDBC API 開發時,最常用的是 ResultSet 介面讀取資料。但 ResultSet 屬於線上資料集,讀取過程中必須維持資料庫連線,僅能在讀取完畢後關閉相關資源,使用上不夠靈活。Java 標準庫其實提供了離線資料集方案——javax.sql.RowSet 介面及其子介面,Sun JDK 內建預設實作,Oracle 等大型資料庫驅動也提供專屬實作。本文以 Sun 預設實作 CachedRowSetImpl 為例,搭配 SQL Server 2000 Northwind 資料庫,示範離線資料集的基本操作與分頁查詢技巧。
| 特性 | ResultSet (線上) | RowSet (離線) |
|---|---|---|
| 連線狀態 | 讀取期間必須保持連線 | 資料填充後可立即關閉連線 |
| 序列化 | 不支援 | 支援,可跨網路傳輸 |
| 捲動/更新 | 視驅動支援度而定 | 標準化支援捲動、更新、分頁 |
| 適用場景 | 即時大量資料串流處理 | 分頁顯示、資料傳遞、離線操作 |
import java.sql.*;
import javax.sql.RowSet;
import com.sun.rowset.CachedRowSetImpl;
public class RowSetDemo {
public static RowSet query(Connection conn, String sql) throws SQLException {
CachedRowSetImpl rowset = new CachedRowSetImpl();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
rowset.populate(rs); // 將線上 ResultSet 資料填充至離線 RowSet
rs.close();
stmt.close();
return rowset; // 此時可安全關閉連線
}
public static void main(String[] args) throws Exception {
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
String url = "jdbc:sqlserver://127.0.0.1:1433;database=Northwind;user=sa;password=huhuiyu";
Connection conn = DriverManager.getConnection(url);
RowSet rs = query(conn, "SELECT * FROM Customers ORDER BY CustomerID");
conn.close(); // 連線關閉後仍可操作 RowSet
while (rs.next()) {
System.out.print(rs.getString(1) + " : ");
System.out.println(rs.getString("CompanyName"));
}
}
}
關鍵步驟:
CachedRowSetImpl 實例ResultSetpopulate(rs) 將資料複製到離線集合ResultSet、Statement 與 ConnectionResultSet 一樣使用 RowSet(next()、getString() 等)CachedRowSetImpl 原生支援分頁,無需手工計算 LIMIT/OFFSET 或使用子查詢:
public static RowSet query(Connection conn, String sql, int pageSize, int pageNumber) throws SQLException {
CachedRowSetImpl rowset = new CachedRowSetImpl();
// 必須使用可捲動的 ResultSet
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet rs = stmt.executeQuery(sql);
rowset.setPageSize(pageSize); // 設定每頁筆數
int skip = (pageNumber - 1) * pageSize + 1; // 計算起始游標位置(1-based)
rowset.populate(rs, skip); // 從指定位置填充
rs.close();
stmt.close();
return rowset;
}
// 使用範例:查詢第 2 頁,每頁 5 筆
RowSet rs = query(conn, "SELECT * FROM Customers ORDER BY CustomerID", 5, 2);
原理:populate(rs, startRow) 會從 ResultSet 的第 startRow 列開始讀取 pageSize 筆資料。搭配 TYPE_SCROLL_SENSITIVE 確保游標可定位。
com.sun.rowset.CachedRowSetImpl 屬 Sun 內部實作,跨 JDK 版本可能變動;生產環境建議使用標準 javax.sql.rowset.RowSetFactory (JDBC 4.1+) 或資料庫廠商提供的實作(如 Oracle oracle.jdbc.rowset.OracleCachedRowSet)RowSet 支援 updateRow()、insertRow()、deleteRow() 等離線修改,稍後可透過 acceptChanges() 批次回寫資料庫,需處理並發衝突RowSet 實作 Serializable,可直接在分層架構間傳遞(如 EJB 遠端介面、Web Service),避免手工轉換 DTO若專案未引入持久層框架(Hibernate、MyBatis 等),善用 JDBC 內建的 RowSet 離線資料集,能優雅解決分頁查詢、連線池釋放、跨層資料傳遞等常見痛點。掌握 CachedRowSetImpl 的基本操作與分頁技巧,是 Java 開發者進階 JDBC 應用的關鍵技能之一。