带有输入参数的 Oracle JDBC 调用 PL/SQL 过程记录表

Posted

技术标签:

【中文标题】带有输入参数的 Oracle JDBC 调用 PL/SQL 过程记录表【英文标题】:Oracle JDBC call PL/SQL procedure with input parameter a table of records 【发布时间】:2016-09-27 13:45:00 【问题描述】:

我正在尝试从 JDBC 调用以下 pl/sql 过程。

create or replace PACKAGE test AS

type testrec_r is record (
 val1 number,
 val2 varchar2(100)
 );

 type testarr_t is table of testrec_r index by binary_integer;

function test_func(i_data in testarr_t, o_sum out number, o_totallength out     number) return number;

END test;

这就是我尝试调用它的方式,但没有成功:

StructDescriptor recDescriptor = StructDescriptor.createDescriptor("test.testrec_r", conn);
    STRUCT[] RECORDS_ARRAY = new STRUCT[2];

    for (int i = 0; i < 2; i++) 
         STRUCT oracle_record = new STRUCT(recDescriptor, conn, new
         Object[] i, "test");
         RECORDS_ARRAY[i] = oracle_record;
    

    CallableStatement stmt = conn.prepareCall(" call TEST.TEST_FUNC(?, ?, ?) ");

    ArrayDescriptor arrayDescriptor = ArrayDescriptor.createDescriptor("TEST.TESTARR_T", conn);
    ARRAY oracle_array = new ARRAY(arrayDescriptor, conn, RECORDS_ARRAY);

    // Bind the input record
    stmt.setArray(1, oracle_array);
    stmt.registerOutParameter(2, Types.NUMERIC);
    stmt.registerOutParameter(3, Types.NUMERIC);
    stmt.executeUpdate();
    double out1 = stmt.getDouble(2);
    double out2 = stmt.getDouble(3);
    return new Object[]  out1, out2 ;

我刚刚读到 oracle jdbc 不支持 pl/sql 结构类型。所以,这失败了“无效的名称模式:test.testrec_r”

如何从 Java 调用此过程?理想情况下只使用 java libray/API,但这似乎几乎不可能,这是将 pl/sql 包包装在简单的 sql 调用中并调用它的最佳解决方法?

P.S 我正在使用 Spring JDBCTemplate 进行数据库连接。

【问题讨论】:

【参考方案1】:

您不能使用 PL/SQL 类型,因为它们只为 PL/SQL 所知(从 12c 开始,这不再严格 - 请参阅 UPD)。此外,在包中创建的任何类型都不能被 java 直接看到。 您应该在架构级别创建 SQL 类型。 SQL 类型对所有人可见并可供所有人使用。

create or replace and compile java source named "ArrayOfRecTest" as 
import java.io.*; 
import java.sql.*; 
import oracle.sql.*; 
import oracle.jdbc.driver.*; 

public class ArrayOfRecTest 
 
    public static void passArrayOfRec() throws SQLException 
     
        Connection conn = new OracleDriver().defaultConnection(); 

        StructDescriptor sd = StructDescriptor.createDescriptor("MYREC_TYPE", conn);
        ArrayDescriptor ad = ArrayDescriptor.createDescriptor("MYRECARR_TYPE", conn);
        STRUCT[] recarr = new STRUCT[2];
        for (int i = 0; i < 2; i++)  recarr[i] = new STRUCT(sd, conn, new Object[] i+1, "value " + (i+1)); 
        ARRAY oracle_array = new ARRAY(ad, conn, recarr);

        CallableStatement stmt = conn.prepareCall(" ? = call testpkg.showArrOfRec(?, ?, ?) ");
        stmt.registerOutParameter(1, Types.INTEGER);
        stmt.setObject(2, oracle_array);
        stmt.registerOutParameter(3, Types.INTEGER);
        stmt.registerOutParameter(4, Types.INTEGER);

        stmt.execute();
        int sizeofArr = stmt.getInt(1);
        int total = stmt.getInt(3);
        int totalLength = stmt.getInt(4);
        System.out.println("passArrayOfRec(total,len)=(" + total + "," + totalLength + ") " + sizeofArr + " records were shown");
    

/

create or replace force type myrec_type as object( id number, value varchar2(100));
/ 
create or replace type myrecarr_type as table of myrec_type;
/

create or replace package testpkg as
    procedure passArrayOfRec as language java name 'ArrayOfRecTest.passArrayOfRec()' ; 
    function showArrOfRec(ra myrecarr_type, total out number, totallength out number) return number;
end testpkg;
/

create or replace package body testpkg as 
    --OP stuff 
    type testrec_r is record (val1 number, val2 varchar2(100));
    type testarr_t is table of testrec_r index by binary_integer;
    function test_func(data in testarr_t, total out number, totallength out number) return number is
    begin
        <<for_each>> for i in data.first..data.last loop
            dbms_output.put_line('data(' || i || ')[val1,val2]=[' || data(i).val1 || ',' || data(i).val2 || ']');
            total := nvl(total,0) + data(i).val1;
            totallength := nvl(totallength,0) + length(data(i).val2);
        end loop for_each;
        return data.count;
    end test_func;
    --end OP stuff

    function showArrOfRec(ra myrecarr_type, total out number, totallength out number) return number is
        data testarr_t; 
    begin
        for i in ra.first..ra.last loop data(i).val1 := ra(i).id; data(i).val2 := ra(i).value; end loop; 
        return test_func(data, total, totalLength);
    end showArrOfRec;
end testpkg;
/

exec testpkg.passArrayOfRec;

输出:

数据(1)[val1,val2]=[1,值 1] 数据(2)[val1,val2]=[2,值 2] passArrayOfRec(total,len)=(3,14) 显示2条记录


更新New in 12cR1: Using PL/SQL Types

【讨论】:

@csm86 这个函数应该返回什么?在您的问题中,我看不到该功能的作用。 对不起,我错误地删除了最后一条评论。这是一个虚拟函数,它返回数组的长度,并在输出参数中输出数字总和和总字符串长度。这就是我想在简单的 sql 中得到的。 @csm86 好的,我更新了函数调用。总数应该不是什么大问题。 感谢您的提示。但你仍然没有得到我的问题。我想将 myrec_type 转换为 testrec_r 并使用这种参数调用 test.test_func'(来自我的示例)。不重写 plsql 功能。我可能想使用复杂的逻辑。所以我认为的主要问题是从 sql 类型到 plsql 类型的双向转换。 @csm86 plsql 上下文中的类型转换完全没有问题。那只是一个循环。您可以使用 sql 类型作为 IN 参数编写包装过程或函数,并在其中执行类型转换。比从 java 调用这个 wrap 函数,而不是直接调用你的函数。它是您的问题的解决方案吗?

以上是关于带有输入参数的 Oracle JDBC 调用 PL/SQL 过程记录表的主要内容,如果未能解决你的问题,请参考以下文章

使用带有布尔输入参数的 PL/SQL 在 oracle 中调用 java 存储过程

ORACLE存储过程怎么调用?

对 Oracle 存储函数的 JDBC 调用 - “结构表”类型的 IN 参数

使用简单的 jdbc 调用将数组作为输入参数传递给 oracle 存储过程

从 0jdbc6 JDBCthin 驱动程序调用具有自定义对象返回类型的 Oracle PL/SQL 过程

JDBC调用存储过程的例子