Oracle 透過 REF CURSOR 實現結果集返回給 Java,需建立 Package 定義游標型別、Function/Procedure 打開游標、Java 端用 CallableStatement 註冊輸出參數並取得 ResultSet。本文提供在 SQL*Plus 建立物件與 Java 調用的完整可執行代碼。
-- 1. 定義游標型別 Package
CREATE OR REPLACE PACKAGE types AS
TYPE cursorType IS REF CURSOR;
END types;
/
-- 2. Function 返回 REF CURSOR
CREATE OR REPLACE FUNCTION sp_ListEmp RETURN types.cursorType AS
l_cursor types.cursorType;
BEGIN
OPEN l_cursor FOR SELECT id, title FROM cf_news ORDER BY id;
RETURN l_cursor;
END;
/
-- 3. Procedure 透過 OUT 參數返回 REF CURSOR
CREATE OR REPLACE PROCEDURE getemps(p_cursor IN OUT types.cursorType) AS
BEGIN
OPEN p_cursor FOR SELECT id, title FROM cf_news ORDER BY id;
END;
/
import java.sql.*;
import oracle.jdbc.driver.*;
public class GetValues {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@127.0.0.1:1521:database";
String sql = "BEGIN :1 := sp_ListEmp; END;"; // 調用 Function
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, "scott", "tiger");
CallableStatement cstmt = conn.prepareCall(sql);
cstmt.registerOutParameter(1, OracleTypes.CURSOR); // 關鍵:註冊 CURSOR 型別
cstmt.execute();
ResultSet rs = (ResultSet) cstmt.getObject(1); // 取得 ResultSet
while (rs.next()) {
System.out.println(rs.getString(1)); // 列印 id
}
cstmt.close();
conn.close();
}
}
String sql = "{CALL getemps(?)}"; // 或 BEGIN getemps(?); END;
CallableStatement cstmt = conn.prepareCall(sql);
cstmt.registerOutParameter(1, OracleTypes.CURSOR);
cstmt.execute();
ResultSet rs = (ResultSet) cstmt.getObject(1);
// 後續處理相同
| 環節 | 關鍵代碼 | 說明 |
|---|---|---|
| Package | TYPE cursorType IS REF CURSOR; | 定義弱類型游標,可對應任意查詢結構 |
| Function | RETURN types.cursorType | 直接返回游標,調用語法 BEGIN :1 := func; END; |
| Procedure | p_cursor IN OUT types.cursorType | OUT 參數返回,調用語法 {CALL proc(?)} |
| JDBC 註冊 | registerOutParameter(idx, OracleTypes.CURSOR) | **必須使用 OracleTypes.CURSOR**,標準 JDBC 無此常數 |
| 取得結果 | (ResultSet) cstmt.getObject(idx) | 轉型為 ResultSet 正常迭代 |
ORA-06550 / PLS-00103:SQL 語法錯誤,檢查分號、BEGIN/END 包裹。Invalid column type:registerOutParameter 未用 OracleTypes.CURSOR 或驅動版本不匹配。Closed ResultSet:呼叫 cstmt.close() 會關閉關聯 ResultSet,**先處理完 ResultSet 再關閉 Statement/Connection**。EXECUTE 權限於 Package/Function/Procedure。SELECT id, title),避免 SELECT * 導致 Java 端欄位索引錯位。FETCH SIZE 調優:stmt.setFetchSize(1000)。掌握 REF CURSOR 雙向互動,能將複雜業務邏輯下沉資料庫層,Java 層專注業務編排,架構更清晰。