从 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 存储过程
使用带有布尔输入参数的 PL/SQL 在 oracle 中调用 java 存储过程