同樣代碼在 Windows + Oracle 10g 單機正常,部署至 RedHat 5.2 64bit + Oracle 10g RAC 叢集後,PreparedStatement.setMaxRows(n) 失效,固定只回傳 10 筆。本文記錄現象、臨時解法與根因分析方向。
// 正常環境:Windows + Oracle 10g 單機
PreparedStatement pstmt = conn.prepareStatement(
"SELECT * FROM (SELECT * FROM ENTITY_HOTLINK WHERE HOTLINKSORTID = ? AND HOTLINKSTATUS = ? ORDER BY HOTLINKORDER ASC, LASTMODIFIEDDATE DESC) WHERE ROWNUM <= ?");
pstmt.setInt(1, sortId);
pstmt.setInt(2, status);
pstmt.setInt(3, count); // 動態設定上限
ResultSet rs = pstmt.executeQuery(); // 正確回傳 count 筆
// 異常環境:RHEL 5.2 64bit + Oracle 10g RAC
// 同樣代碼,無論 count 設多大,永遠只回傳 10 筆
// 將 setMaxRows 改為直接拼接在 SQL 字串中
String sql = "SELECT * FROM (SELECT * FROM ENTITY_HOTLINK WHERE HOTLINKSORTID = ? AND HOTLINKSTATUS = ? ORDER BY HOTLINKORDER ASC, LASTMODIFIEDDATE DESC) WHERE ROWNUM <= " + count;
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, sortId);
pstmt.setInt(2, status);
// 不再 setInt(3, count)
ResultSet rs = pstmt.executeQuery(); // 正常回傳 count 筆
缺點:失去 PreparedStatement 預編譯優勢、SQL 注入風險 (此處 count 為內部變數相對安全)、計畫無法重用。
ojdbc6.jar (JDK6) 或更新版本,舊版 ojdbc14.jar 在 RAC 下行為異常。setMaxRows 可能在某節點生效、另一節點不生效。WebLogic/Tomcat 連線池可能包裝 PreparedStatement 導致 setMaxRows 未正確傳遞到底層驅動。Doc ID 726988.1 記載類似問題,建議升級至 10.2.0.5 或 11.2.0.4+。_optimizer_max_rows:RAC 環境可能被隱性參數覆蓋。SELECT * FROM V$SESSION_CONNECT_INFO WHERE SID = ... 看 CLIENT_DRIVERStatement.setFetchSize() 替代 setMaxRows()ROWNUM <= :bind 綁定變數 (如臨時解法但用綁定變數)-Doracle.jdbc.Trace=true) 觀察驅動行為SQLNET.ORA、LISTENER.ORA 一致性WHERE ROWNUM <= :max_rows 綁定變數,而非依賴 JDBC setMaxRowsFetchSize 控制網路往返筆數,MaxRows 控制總筆數上限ONS (Oracle Notification Service) 支援 Fast Connection FailoverV$SQL_SHARED_CURSOR 確認計畫重用情況此問題極大機率為 JDBC Driver 與 RAC 環境不相容 導致 setMaxRows 未正確傳遞至伺服器。將上限參數化放入 SQL 綁定變數 是最穩健、跨環境相容的解法,同時保留 PreparedStatement 預編譯優勢。