从 Java 调用带有数组输出参数的 Oracle 存储过程

Posted

技术标签:

【中文标题】从 Java 调用带有数组输出参数的 Oracle 存储过程【英文标题】:call Oracle stored procedure with array output argument from Java 【发布时间】:2019-02-06 11:36:20 【问题描述】:

我从 Java 调用 plsql 过程时遇到问题。程序包如下:(架构job_runner和连接用户/架构不一样):

create or replace package  test_package_for_sp as

  type some_record_type is record
  (
      field_number      number,
      field_varchar2    varchar2 (128),
      field_date        date
  );

  type some_table_type is table of some_record_type;


  procedure proc_table (p_card_bin    in     varchar2,
                        p_date        in     date default null,
                        p_out_table      out some_table_type);

然后我尝试使用 callableStatement 从 Java 调用它:

    final String typeTableList = "SOME_TABLE_TYPE";

    CallableStatement cs = null;
    try (Connection con = dataSource.getConnection()) 
        con.setSchema("JOB_RUNNER");

        cs = con.prepareCall("call job_runner.test_package_for_sp.proc_table(?, ?, ?)");

        cs.setString(1, "54867321");
        cs.setDate(2, Date.valueOf(ZonedDateTime.now().minusDays(200).toLocalDate()));
        cs.registerOutParameter(3, Types.ARRAY, typeTableList);

        cs.execute();
     finally 
        if (cs != null)
            cs.close();
    

错误引发:

java.sql.SQLException: invalid name pattern: <connection_scheme>.SOME_TABLE_TYPE

    at oracle.jdbc.oracore.OracleTypeADT.initMetadata11_2(OracleTypeADT.java:764)
    at oracle.jdbc.oracore.OracleTypeADT.initMetadata(OracleTypeADT.java:479)
    at oracle.jdbc.oracore.OracleTypeADT.init(OracleTypeADT.java:443)

如果我将 typeTableLis 中的值从值 SOME_TABLE_TYPE 更改为 带有包和方案的完整路径JOB_RUNNER.TEST_PACKAGE_FOR_SP.SOME_TABLE_TYPE 异常更改为:

java.sql.SQLSyntaxErrorException: ORA-01948: identifier's name length (35) exceeds maximum (30)
ORA-06512: at "SYS.DBMS_PICKLER", line 18
ORA-06512: at "SYS.DBMS_PICKLER", line 58
ORA-06512: at line 1


    at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:494)

有人知道如何从java调用这个过程吗?

【问题讨论】:

1) 将您的类型创建为 SQL 类型 ("create type some_record_type as object (..);") 2) 使用 oracle java arraydescriptor,在您的过程中识别并传递类型; @Ychdziu Arraydescriptor 在内部被调用(在 oracle.sql.ArrayDescriptor.createDescriptor(ArrayDescriptor.java:79))。并且这个类已被弃用。你能举一些例子如何识别数组描述符中的类型并将其传递给过程吗? 【参考方案1】:

您可以将 JDBC 连接属性 "oracle.jdbc.createDescriptorUseCurrentSchemaForSchemaName" 设置为 "true",然后将架构切换为 "job_runner" (ALTER SESSION SET CURRENT_SCHEMA=job_runner) 并将 TEST_PACKAGE_FOR_SP.SOME_TABLE_TYP 用于 typeTableList。

【讨论】:

感谢评论,但这同样不起作用。错误是:java.sql.SQLException: invalid name pattern: TEST_PACKAGE_FOR_SP.SOME_TABLE_TYPE at oracle.jdbc.oracore.OracleTypeADT.initMetadata11_2(OracleTypeADT.java:764) at oracle.jdbc.oracore.OracleTypeADT.initMetadata(OracleTypeADT.java:479) at oracle.jdbc.oracore.OracleTypeADT.init(OracleTypeADT.java:443) at oracle.sql.ArrayDescriptor.initPickler(ArrayDescriptor.java:1499) 您使用的是哪个版本的驱动程序? 11.2 还是 12.1 还是 12.2? 18c可能吗?做 java -jar ojdbcX.jar 找出来。 驱动版本:12.2.0.1 我怀疑数据库版本比 (12.2) 旧,因为 Oracle 中标识符的最大长度在 12.2 中从 30 扩展到 128。你的数据库版本是多少? 是的,甲骨文 11.2.0.4.0。谢谢,好像和这个有关。【参考方案2】:

方法之一:

package testproject;

import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Types;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import oracle.sql.ARRAY;

import oracle.jdbc.OracleCallableStatement;

import oracle.sql.ArrayDescriptor;

    public class MainClass 
    public MainClass() 
        super();
    

    public static void main(String[] args) 
        MainClass mainClass = new MainClass();

        Connection conn = null;

        ArrayDescriptor des = null;

        try 

            Object[] obj1 =  1, "2017-01-01 10:12:10", 200 ;
            Object[] obj2 =  2, "2017-06-01 10:12:10", 600 ;
            Object[] obj3 =  3, "2017-08-01 10:12:10", 990 ;

            conn =
DriverManager.getConnection("jdbc:oracle:thin:@<DB_HOST>",
                            "<user>", "<pass>");

          try 
              des = ArrayDescriptor.createDescriptor("AJ_TEST_OBJ_TBL", conn);
           catch (SQLException e) 
              System.out.println("Arraydesc went wrong.");
              System.out.println(e.getStackTrace());
          

          ARRAY nArray =
              new ARRAY(des, conn, new Object[]  obj1, obj2, obj3 );

            OracleCallableStatement pstmt =
                (OracleCallableStatement)conn.prepareCall("begin 
aj_test_array_pck.print_tbl_parameters(?,?); end;");

            pstmt.setArray(1, nArray);
            pstmt.registerOutParameter(2, Types.VARCHAR);
            pstmt.execute();

            String status = pstmt.getString(2);
            System.out.println("Status: " + status);

            pstmt.close();
            conn.close();
         catch (SQLException e) 
            System.out.println("Oops with select");
            System.out.println(e.getStackTrace());
         catch (ClassNotFoundException e) 
            System.out.println("Oops with class");
        
    


其中“AJ_TEST_OBJ_TBL”是一个 SQL 类型的对象。

【讨论】:

是的,我在某处看到了这个例子,但它有 IN 数组参数的例子,而不是我需要的 OUT 正如我之前所说,ArrayDescriptor.createDescriptor 已在内部调用 in 参数是定义的数组,out 只是一个简单的 varchar,结果为 E/S,只是为了看看程序是否完成了它的工作。 主要方面是,定义你的类型不是PL/SQL类型,而是SQL类型。

以上是关于从 Java 调用带有数组输出参数的 Oracle 存储过程的主要内容,如果未能解决你的问题,请参考以下文章

oracle中怎么执行带有输出参数的存储过程,在程序中我知道怎么调用,

使用 Python 和 Cx_Oracle 调用带有 XMLTYPE 输入和输出参数的 Oracle 存储过程

oracle存储过程输入参数能否为空

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

Hibernate调用带有输入参数,输出参数为cursor的存储过程

从 Powershell 调用带有数组参数的构造函数