java.sql.SQLException: After end of result set
嘗試存取已關閉或已耗盡的 ResultSet 物件。常見於同一 Connection 或 Statement 重複執行查詢,導致前一個 ResultSet 隱性關閉。
// 第一次查詢
String sql1 = "SELECT stateId FROM state WHERE stateMessage = ?";
ResultSet rs1 = con.executeQuery(sql1);
while (rs1.next()) {
stateId = String.valueOf(rs1.getInt(1));
}
// 第二次查詢 - 同一 Statement/Connection
String sql2 = "SELECT adminId FROM admin WHERE adminRealName = ?";
ResultSet rs2 = con.executeQuery(sql2); // 隱性關閉 rs1!
while (rs2.next()) {
adminId = String.valueOf(rs1.getInt(1)); // 錯誤:仍使用 rs1 而非 rs2
}
Statement 執行新查詢,舊 ResultSet 自動關閉。Connection 在自動認交模式下,新 Statement 可能導致舊 ResultSet 失效(視驅動實作)。ResultSet 呼叫任何方法拋出此異常。// 使用 try-with-resources (Java 7+)
try (PreparedStatement ps1 = con.prepareStatement(sql1);
ResultSet rs1 = ps1.executeQuery()) {
while (rs1.next()) {
stateId = String.valueOf(rs1.getInt(1));
}
}
try (PreparedStatement ps2 = con.prepareStatement(sql2);
ResultSet rs2 = ps2.executeQuery()) {
while (rs2.next()) {
adminId = String.valueOf(rs2.getInt(1)); // 正確使用 rs2
}
}
PreparedStatement。ResultSet、Statement、Connection 正確關閉。maxOpenPreparedStatements 避免語句快取導致非預期關閉。某些連線池(如 DBCP、HikariCP)預設啟用 Statement Cache,prepareStatement 可能回傳快取的 Statement 物件。若前一次查詢的 ResultSet 尚未完全消費就再次呼叫,會觸發隱性關閉。解決:
- 關閉 Statement Cache(效能代價)
- 確保每次查詢完整消費 ResultSet 後再歸還連線
- 使用 SET HOLD_CURSORS_OVER_COMMIT 相容模式(視資料庫支援)