多數情況下應避免從預存程序回傳結果集,改用應用層組裝 SQL。僅在以下情境必要:
CREATE TABLE HOTLINE (
COUNTRY VARCHAR2(50),
PNO VARCHAR2(50)
);
INSERT INTO HOTLINE VALUES ('Taiwan', '0800-123-456');
INSERT INTO HOTLINE VALUES ('USA', '1-800-555-0199');
COMMIT;
CREATE OR REPLACE PACKAGE PKG_HOTLINE IS
TYPE HOTLINE_CURSOR_TYPE IS REF CURSOR;
FUNCTION GET_HOTLINE RETURN HOTLINE_CURSOR_TYPE;
END;
/
CREATE OR REPLACE PACKAGE BODY PKG_HOTLINE IS
FUNCTION GET_HOTLINE RETURN HOTLINE_CURSOR_TYPE IS
C_HOTLINE HOTLINE_CURSOR_TYPE;
BEGIN
OPEN C_HOTLINE FOR SELECT * FROM HOTLINE;
RETURN C_HOTLINE;
END;
END;
/
Oracle 內建 SYS_REFCURSOR 為弱類型 REF CURSOR,可直接使用無需自訂類型。
VAR RC REFCURSOR;
EXEC :RC := PKG_HOTLINE.GET_HOTLINE;
PRINT RC;
public void queryWithJdbc() {
String sql = "{? = CALL PKG_HOTLINE.GET_HOTLINE()}";
try (Connection conn = getConnection();
CallableStatement stmt = conn.prepareCall(sql)) {
stmt.registerOutParameter(1, OracleTypes.CURSOR);
stmt.execute();
// 標準 Oracle 驅動需轉型
ResultSet rs = ((OracleCallableStatement) stmt).getCursor(1);
while (rs.next()) {
System.out.println("Country: " + rs.getString(1) + " | Phone: " + rs.getString(2));
}
} catch (Exception e) {
e.printStackTrace();
}
}
// C3P0 包裝的 CallableStatement 不支援 getCursor()
ResultSet rs = (ResultSet) stmt.getObject(1); // 必須用 getObject
關鍵差異:直連用getCursor(1);C3P0/DBCP 等連線池必須用getObject(1),否則拋ClassCastException。
PROCEDURE GET_MULTI(
CURSOR1 OUT SYS_REFCURSOR,
CURSOR2 OUT SYS_REFCURSOR
) IS
BEGIN
OPEN CURSOR1 FOR SELECT * FROM TABLE_A;
OPEN CURSOR2 FOR SELECT * FROM TABLE_B;
END;
JDBC 依序 getObject(1), getObject(2) 取得多個 ResultSet。
SYS_REFCURSOR,減少自訂類型維護finally 區塊關閉 ResultSet/Statement/ConnectiongetObject() 取得游標