問題描述 ResultSet 表示 select 語句的查詢結果集。ResultSet 對象具有指向其當前數據行的指針, 最初,指針被置於第一行記錄之前,通過 next() 方法可以將指針移動到下一行記錄。 next() 方法在 ResultSet 對象沒有一行記錄時返回 false ,因此可以在 ...
問題描述
ResultSet 表示 select 語句的查詢結果集。ResultSet 對象具有指向其當前數據行的指針, 最初,指針被置於第一行記錄之前,通過 next() 方法可以將指針移動到下一行記錄。
next() 方法在 ResultSet 對象沒有一行記錄時返回 false ,因此可以在 while 迴圈中使用它來遍歷結果集,也可以利用該方法判斷結果集是否為空。
示例代碼如下:
//此處省略連接資料庫的代碼...
Statement stmt =conn.createStatement();
ResultSet rs =stmr.executeQuery("select * from Test");
if(rs.next()){
System.out.println("結果集不為空!");
}
else{
System.out.println("結果集為空!");
}
此時出現第一個坑:Java 的 ResultSet 對象,預設是不可更新的,僅有一個向前移動的指針。
因此,只能遍歷它一次,並且只能按從第一行到最後一行的順序進行。
當你使用了 rs.next() 進行判斷後,會出現第一行數據丟失的情況。
這也是我一開始遇到的問題。
深究問題
ResultSet 的 Type 屬性
遇到問題後我第一想法是在搜索引擎上搜索相關解決辦法,但看了一圈具體有以下“解法”:
調用 rs.last() 方法,以獲取 ResultSet中 記錄的總數,然後調用 rs.beforeFirst() 方法將游標移回到第一條記錄前面
這種方法看上去可行,但當我實際修改後運行,卻出現報錯
Operation not allowed for a result set of type ResultSet.TYPE_FORWARD_ONLY.
這又是什麼原因呢?
出現這個報錯的主要原因是:
ResultSet.TYPE_FORWARD_ONLY 類型的 ResultSet 只允許向前遍歷,不支持訪問先前的記錄或確定其大小。因此,使用 last() 和 getRow() 等方法都是不可行。
而 ResultSet 的 Type 屬性有如下幾種:
參數類型 | 說明 |
---|---|
ResultSet.TYPE_FORWORD_ONLY | 結果集的游標只能向下滾動 |
ResultSet.TYPE_SCROLL_INSENSITIVE | 結果集的游標可以上下移動,當資料庫變化時,當前結果集不變 |
ResultSet.TYPE_SCROLL_SENSITIVE | 返回可滾動的結果集,當資料庫變化時,當前結果集同步改變 |
當 Statement stmt = conn.createStatement();
中 的 createStatement() 預設時等價於: createStatement(ResultSet.TYPE_FORWORD_ONLY,ResultSet.CONCUR_READ_ONLY);
也就是結果集的游標只能向下滾動
所以才會出現 Operation not allowed for a result set of type ResultSet.TYPE_FORWARD_ONLY. 的報錯
也就是 ResultSet 預設情況下,只能使用 next() 方法向前逐行移動游標,而不支持 last()、first() 以及 absolute() 等方法,如果要使用 last()、absolute() 等方法,必須在由 Connection 生成 Statement 時指定相應的參數,格式如下:
Statement stmt =conn.ctrateStatement(游標類型,記錄更新許可權);
解決辦法
-
手動指定游標類型
Statement stmt =conn.createStatement(ResultSet.TYPE_SCOLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
-
將 ResultSet 的內容複製到一個 List 中,然後檢查 List 是否為空
List<Object[]> results = new ArrayList<>(); while (rs.next()) { int columnCount = rs.getMetaData().getColumnCount(); Object[] row = new Object[columnCount]; for (int i = 1; i <= columnCount; i++) { row[i - 1] = rs.getObject(i); } results.add(row); } if (!results.isEmpty()) { // ResultSet不為空,可以執行讀取操作 } else { // ResultSet為空,不執行讀取操作 }
請註意,在實際生產環境中,請考慮數據量和記憶體使用情況,因為將所有記錄複製到List中可能對記憶體產生很大影響。
-
定義 一個計數變數 i ,每次 next() 則 ++i ,在 while() 迴圈結束後判斷 i 是否小於等於 0
int i = 0; while (resultSet.next()){ System.out.println(...); ++i; } if(i<=0) System.out.println(...);