使用JDBC将SYS_REFCURSOR作为IN参数调用PL / SQL过程

时间:2014-11-20 23:01:46

标签: java jdbc parameters plsql cursor

我试图了解如何调用PL / SQL过程,该过程将SYS_REFCURSOR作为IN参数。

考虑以下PL / SQL过程:

print_cursor_contents(myCursor SYS_REFCURSOR , row_count OUT NUMBER);

在将值绑定到IN参数时,我使用setXXX方法?

给我一​​个带有单独游标记录字段的java类,因为它的成员和这个类的实例数组似乎是表示plsql CURSOR的正确方法。当我这样做时,我得到一个SQLException:

我使用了以下set方法

         callStmt.setObject(1, curRec);

以下是使用上述声明时的例外情况:

Exception occured in the database
Exception message: Invalid column type
java.sql.SQLException: Invalid column type
    at oracle.jdbc.driver.OraclePreparedStatement.setObjectCritical(OraclePreparedStatement.java:8921)
    at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:8396)
    at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:9176)
    at oracle.jdbc.driver.OracleCallableStatement.setObject(OracleCallableStatement.java:5024)
    at oracle.jdbc.driver.OraclePreparedStatementWrapper.setObject(OraclePreparedStatementWrapper.java:234)
    at com.rolta.HrManager.printMaxSalAllDept(HrManager.java:1022)
    at com.rolta.HrManager.main(HrManager.java:1116)
Database error code: 17004

1 个答案:

答案 0 :(得分:2)

  

给我一​​个带有单独游标记录字段的java类,因为它的成员和这个类的实例数组似乎是表示plsql CURSOR的正确方法。

我不同意。

如果您有一个存储的函数或过程要么返回一个引用游标,要么将一个引用游标作为OUT参数,那么引用游标就会以ResultSet的形式从JDBC中出来。因此,如果可以使用SYS_REFCURSOR参数调用存储过程,我会怀疑ResultSet将是您需要传递的内容。

事实上,我的怀疑得到了证实。如果您查看Oracle对CallableStatement OracleCallableStatement的扩展,它会从其超级接口OraclePreparedStatement继承setCursor(int, ResultSet)方法。因此,您可以将CallableStatement转换为OracleCallableStatement,调用setCursor方法,然后离开。

除了这种方法实际上不起作用。

如果您尝试在setCursor上致电OracleCallableStatement,则会收到例外java.sql.SQLException: Unsupported feature

您可以尝试使用setObject来调用ResultSet,但只会获得另一个java.sql.SQLException: Invalid column type例外。

这是一个可以运行以验证这两种情况的测试类。它调用一个存储过程来获取引用游标(因此是ResultSet),然后尝试将其传递给另一个:

import java.sql.*;
import oracle.jdbc.OracleTypes;
import oracle.jdbc.OracleCallableStatement;

public class JavaRefCursorTest {
    public static void main(String[] args) throws Exception {
        Connection conn = DriverManager.getConnection(
                "jdbc:oracle:thin:@localhost:1521:XE", "user", "password");

        try (CallableStatement cstmt1 = conn.prepareCall(
                "{ call java_ref_curs_test.get_ref_cursor(?)}")) {
            cstmt1.registerOutParameter(1, OracleTypes.CURSOR);
            cstmt1.execute();

            try (ResultSet rSet = (ResultSet)cstmt1.getObject(1)) {
                try (CallableStatement cstmt2 = conn.prepareCall(
                        "{ call java_ref_curs_test.print_refcursor(?)}")) {

                    // Uncomment the next line to call setCursor:
                    // ((OracleCallableStatement)cstmt2).setCursor(1, rSet); 

                    // Uncomment the next line to call setObject:
                    // cstmt2.setObject(1, rSet);

                    cstmt2.execute();
                }
            }
        }
    }
}

java_ref_curs_test中的两个过程采用一个SYS_REFCURSOR参数:get_ref_cursor返回一个引用游标,print_refcursor将一个作为参数但不对其执行任何操作。 )

那么,您应该使用哪种setXXX方法?我不会说他们。你所要求的是不可能直接的。

仍然可以调用此过程,但是您必须在PL / SQL中创建引用游标,而不是在Java中,然后将其传递给您的过程。

例如,我可以使用以下PL / SQL块来调用上面示例中使用的两个过程:

DECLARE
   l_curs   SYS_REFCURSOR;
BEGIN
   java_ref_curs_test.get_ref_cursor(l_curs);
   java_ref_curs_test.print_refcursor(l_curs); 
END;

你可以很容易地从JDBC运行它:把它放在一个字符串中并传递给Statement.executeUpdate()