java.sql.SQLException: ORA-01000: maximum open cursors exceeded
表示單一進程開啟的游標數超過 open_cursors 參數限制(預設 300)。
Connection.createStatement()、prepareStatement()、prepareCall() 在資料庫端各對應一個開啟游標。// ❌ 錯誤:迴圈內重複建立 Statement,無 close
for (int i=0; i<1000; i++) {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT ...");
// 處理 rs
// 遺漏 rs.close(), stmt.close()
}
// ✅ 自動關閉 Connection/Statement/ResultSet
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, id);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) { /* 處理 */ }
}
} catch (SQLException e) { /* 處理異常 */ }
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while (rs.next()) { /* 處理 */ }
} finally {
if (rs != null) try { rs.close(); } catch (SQLException ignore) {}
if (ps != null) try { ps.close(); } catch (SQLException ignore) {}
if (conn != null) try { conn.close(); } catch (SQLException ignore) {}
}
PreparedStatement,迴圈內僅 setParameter + executeUpdate/executeQuery,結束後一次 close。addBatch() + executeBatch(),大幅減少網路往返與游標開啟次數。maxPoolSize × open_cursors < 資料庫端 processes 參數,避免連線耗盡。TO_DATE 字串拼接導致硬解析與計畫不共享。| 錯誤類型 | 表徵 | 修正 |
|---|---|---|
| 僅會 createStatement | 不關閉、不重用、不懂 PreparedStatement | 改用 PreparedStatement + try-with-resources |
| 迴圈內建 Statement | 每次迴圈開新游標,舊游標洩漏 | 提至迴圈外,或使用批次 API |
| 字串拼接 SQL | SQL 注入風險、硬解析、計畫不共享 | 全面改用綁定變數 |
| ResultSet 未關閉 | 游標持有、記憶體洩漏 | 同 Statement 生命週期綁定關閉 |