java.sql.SQLException: ORA-01000: maximum open cursors exceeded
表示當前 Session 開啟的游標數超過 OPEN_CURSORS 參數預設值 (通常 300)。
每次呼叫 conn.createStatement()、conn.prepareStatement()、stmt.executeQuery() 都會在資料庫端開啟一個游標。若在迴圈中重複建立且未關閉,游標迅速耗盡。
// ❌ 錯誤:迴圈內重複建立 Statement,未關閉
for (String id : ids) {
Statement stmt = conn.createStatement(); // 每次新游標
ResultSet rs = stmt.executeQuery("SELECT * FROM t WHERE id = " + id);
// 處理 rs...
// 缺少 rs.close(), stmt.close()
}
// ✅ 正確:PreparedStatement 重複使用
String sql = "SELECT * FROM t WHERE id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
for (String id : ids) {
pstmt.setString(1, id);
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) { /* 處理 */ }
} // rs 自動關閉
}
} // pstmt 自動關閉
try (Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, 100);
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) { /* ... */ }
}
} // 所有資源自動關閉,即使發生異常
maxTotal ≤ Oracle PROCESSES / SESSIONS 限制。testOnBorrow、validationQuery="SELECT 1 FROM DUAL" 避免殭屍連線。removeAbandonedTimeout 回收洩漏連線 (Druid/HikariCP/PoolingDataSource)。| 調大 OPEN_CURSORS | 治本:修復程式碼 |
|---|---|
| 掩蓋資源洩漏 Bug | 徹底釋放游標,避免記憶體/臨時段洩漏 |
| 延遲 OOM / 共用池壓力 | 減少 PGA/UGA 佔用,提升並發能力 |
| 生產環境參數變更需重啟 | 程式碼修正熱部署即時生效 |
| 無法發現邏輯錯誤 (如死循環建立 Statement) | Code Review / SonarQube 可靜態檢測 |
-- 當前 Session 開啟游標數
SELECT a.value AS open_cursors, s.sid, s.username, s.program
FROM v$sesstat a
JOIN v$statname b ON a.statistic# = b.statistic#
JOIN v$session s ON a.sid = s.sid
WHERE b.name = 'opened cursors current'
ORDER BY a.value DESC;
-- 系統參數
SHOW PARAMETER open_cursors;
Statement、PreparedStatement、ResultSet 是否在 finally 或 try-with-resources 中關閉?PreparedStatement 綁定變數重用。addBatch() / executeBatch() 減少往返?