使用 java SimpleJdbcCall 调用带有参数类型 "is table of varchar2" 的过程 oracle
Call procedure oracle with out parameters type "is table of varchar2" using java SimpleJdbcCall
我创建了一个带有 2 个参数的过程 oracle,其中一个是输出参数类型 TABLE OF VARCHAR2
。如何在 java 中调用它并得到结果?
我创建的测试程序如下:
/* creating package with specs */
create or replace PACKAGE PACK1 AS
TYPE name_array IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER;
PROCEDURE proc_filter_and_return_array( p_name_in IN VARCHAR2, p_name_out_array OUT name_array );
END PACK1;
/* creating package body with procedure */
create or replace PACKAGE BODY PACK1
as
PROCEDURE proc_filter_and_return_array(
p_name_in IN VARCHAR2,
p_name_out_array OUT name_array
)IS
CURSOR c_table1_select is
select name FROM table1_test where name like '%' || p_name_in || '%';
v_index NUMBER := 0;
BEGIN
FOR x IN c_table1_select
LOOP
p_name_out_array( v_index ) := x.name;
v_index := v_index + 1;
END LOOP;
END proc_filter_and_return_array;
END PACK1;
当我在 oracle 中测试它时,我使用以下代码成功获得:
DECLARE
p_name_array pack1.name_array;
BEGIN
pack1.proc_filter_and_return_array(p_name_in => 'name_to_filter', p_name_out_array => p_name_array);
dbms_output.put_line(' number from table: ' || p_name_array(1) );
END;
但是在java中我遇到了一些错误,我是这样调用程序的:
SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withCatalogName("PACK1")
.withProcedureName("PROC_FILTER_AND_RETURN_ARRAY")
.declareParameters( new SqlParameter("P_NAME_IN", Types.VARCHAR) )
.declareParameters( new SqlOutParameter("P_NAME_OUT_ARRAY", Types.ARRAY, "PACK1.NAME_ARRAY" ));
MapSqlParameterSource map = new MapSqlParameterSource();
map.addValue("P_NAME_IN", "name_to_filter");
Map<String, Object> result = simpleJdbcCall.execute(map);
所以我在 运行 上从 java:
得到了这个
org.springframework.jdbc.UncategorizedSQLException: CallableStatementCallback; uncategorized SQLException for SQL [{call PACK1.PROC_FILTER_AND_RETURN_ARRAY(?, ?)}];
SQL state [99999]; error code [17074]; invalid name pattern: PACK1.NAME_ARRAY; nested exception is java.sql.SQLException: invalid name pattern: PACK1.NAME_ARRAY] with root cause
java.sql.SQLException: invalid name pattern: PACK1.NAME_ARRAY
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)
at oracle.sql.ArrayDescriptor.<init>(ArrayDescriptor.java:274)
at oracle.sql.ArrayDescriptor.createDescriptor(ArrayDescriptor.java:127)
at oracle.sql.ArrayDescriptor.createDescriptor(ArrayDescriptor.java:79)
at oracle.jdbc.driver.NamedTypeAccessor.otypeFromName(NamedTypeAccessor.java:83)
at oracle.jdbc.driver.TypeAccessor.initMetadata(TypeAccessor.java:76)
at oracle.jdbc.driver.T4CCallableStatement.allocateAccessor(T4CCallableStatement.java:599)
at oracle.jdbc.driver.OracleCallableStatement.registerOutParameterInternal(OracleCallableStatement.java:201)
at oracle.jdbc.driver.OracleCallableStatement.registerOutParameter(OracleCallableStatement.java:240)
at oracle.jdbc.driver.OracleCallableStatementWrapper.registerOutParameter(OracleCallableStatementWrapper.java:1243)
at com.zaxxer.hikari.pool.HikariProxyCallableStatement.registerOutParameter(HikariProxyCallableStatement.java)
at org.springframework.jdbc.core.CallableStatementCreatorFactory$CallableStatementCreatorImpl.createCallableStatement(CallableStatementCreatorFactory.java:188)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:1090)
at org.springframework.jdbc.core.JdbcTemplate.call(JdbcTemplate.java:1147)
at org.springframework.jdbc.core.simple.AbstractJdbcCall.executeCallInternal(AbstractJdbcCall.java:412)
at org.springframework.jdbc.core.simple.AbstractJdbcCall.doExecute(AbstractJdbcCall.java:372)
at org.springframework.jdbc.core.simple.SimpleJdbcCall.execute(SimpleJdbcCall.java:198)
不幸的是,我无法更改客户端数据库中的任何内容 :( 所以我无法更改声明 TYPE name_array IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER;
,我需要在 java、spring 中构建应用程序启动。有没有办法在 oracle 上不更改程序和包的情况下执行此操作。
我做错了什么?提前致谢。
one of them is a out parameter type TABLE OF VARCHAR2
你错了,其中一个是 TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER
,它是一个关联数组,你不能使用 JDBC.
映射到这种类型
见 or this answer。
- 您需要使用
CREATE TYPE
在 SQL 范围内定义数据类型(而不是在包中的 PL/SQL 范围内),因为 JDBC 只能使用 SQL 定义的数据类型。
- 第 1 点的结果是 JDBC 不支持关联数组,因为它们只是 PL/SQL 数据类型,您需要使用集合(不像 C#,它只支持关联数组数组,不支持集合)。因此,您需要从类型中删除
INDEX BY
子句。
在包外声明类型使用:
CREATE TYPE name_array IS TABLE OF VARCHAR2(50);
然后从包中删除关联数组声明并改用新的集合类型。之后可能会有更多的错误需要调试;但它永远不会使用关联数组工作。
unfortunately, I couldn't change anything in client's database :( so I can't change the declaration TYPE name_array IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER;
and I need to build a application in java, spring boot. Is there some way to do this without change procedure and package on oracle.
您可以尝试通过调用匿名 PL/SQL 块(依次调用程序包)将关联数组转换为 VARRAY
例如 SYS.ODCIVARCHAR2LIST
:
DECLARE
v_name_assoc_array PACK1.NAME_ARRAY;
v_name_list SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST();
v_idx BINARY_INTEGER;
BEGIN
PACK1.proc_filter_and_return_array( :p_name_in, v_name_assoc_array );
v_idx := v_name_assoc_array.FIRST;
WHILE v_idx IS NOT NULL LOOP
v_name_list.EXTEND;
v_name_list(v_name_list.COUNT) := v_name_assoc_array(v_idx);
v_idx := v_name_assoc_array.NEXT(v_idx);
END LOOP;
:p_name_out_array := v_name_list;
END;
/
我来这里是为了展示代码,因为 MTO 已经回答了我。
从 java 调用匿名 PL/SQL 块并获得 return 是最好的解决方案。
终于成功得到下面的代码了:
// declaring a plsql block calling procedure and treating out return parameters
String plSql = " DECLARE "+
" p_name_array PACK1.NAME_ARRAY "+
" p_name_out_array_return SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST(); "+
" v_idx BINARY_INTEGER; "+
" BEGIN "+
" "+
" pack1.proc_filter_and_return_array( p_name_in => ? , p_name_out_array => p_name_array); "+
" "+
" v_idx := p_name_array.first; "+
" WHILE v_idx IS NOT NULL LOOP "+
" p_name_out_array_return.extend; "+
" p_name_out_array_return(idx + 1 ) := p_name_array(idx); "+
" v_idx := p_name_array.next(v_idx); "+
" END LOOP; "+
" "+
" ? := p_name_out_array_return; "+
" END; ";
// calling plsql from jdbcTemplate
jdbcTemplate.execute( new CallableStatementCreator(){
@Override
public CallableStatement createCallableStatement(Connection con) throws SQLException {
CallableStatement cs = con.prepareCall(plSql);
cs.setString(1, "value to first parameter ? in plsql");
// registring out second param p_name_out_array_return used in plsql
cs.registerOutParameter(2, Types.ARRAY , SYS.ODCIVARCHAR2LIST);
return cs;
}
} , new CallableStatementCallback<Object>(){
@Override
public Object doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
cs.execute();
// do something with result out param 2
Arrays.asList((Object[])cs.getArray(2).getArray()).forEach(System.out::println);
return null;
}
} );
我创建了一个带有 2 个参数的过程 oracle,其中一个是输出参数类型 TABLE OF VARCHAR2
。如何在 java 中调用它并得到结果?
我创建的测试程序如下:
/* creating package with specs */
create or replace PACKAGE PACK1 AS
TYPE name_array IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER;
PROCEDURE proc_filter_and_return_array( p_name_in IN VARCHAR2, p_name_out_array OUT name_array );
END PACK1;
/* creating package body with procedure */
create or replace PACKAGE BODY PACK1
as
PROCEDURE proc_filter_and_return_array(
p_name_in IN VARCHAR2,
p_name_out_array OUT name_array
)IS
CURSOR c_table1_select is
select name FROM table1_test where name like '%' || p_name_in || '%';
v_index NUMBER := 0;
BEGIN
FOR x IN c_table1_select
LOOP
p_name_out_array( v_index ) := x.name;
v_index := v_index + 1;
END LOOP;
END proc_filter_and_return_array;
END PACK1;
当我在 oracle 中测试它时,我使用以下代码成功获得:
DECLARE
p_name_array pack1.name_array;
BEGIN
pack1.proc_filter_and_return_array(p_name_in => 'name_to_filter', p_name_out_array => p_name_array);
dbms_output.put_line(' number from table: ' || p_name_array(1) );
END;
但是在java中我遇到了一些错误,我是这样调用程序的:
SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withCatalogName("PACK1")
.withProcedureName("PROC_FILTER_AND_RETURN_ARRAY")
.declareParameters( new SqlParameter("P_NAME_IN", Types.VARCHAR) )
.declareParameters( new SqlOutParameter("P_NAME_OUT_ARRAY", Types.ARRAY, "PACK1.NAME_ARRAY" ));
MapSqlParameterSource map = new MapSqlParameterSource();
map.addValue("P_NAME_IN", "name_to_filter");
Map<String, Object> result = simpleJdbcCall.execute(map);
所以我在 运行 上从 java:
得到了这个org.springframework.jdbc.UncategorizedSQLException: CallableStatementCallback; uncategorized SQLException for SQL [{call PACK1.PROC_FILTER_AND_RETURN_ARRAY(?, ?)}];
SQL state [99999]; error code [17074]; invalid name pattern: PACK1.NAME_ARRAY; nested exception is java.sql.SQLException: invalid name pattern: PACK1.NAME_ARRAY] with root cause
java.sql.SQLException: invalid name pattern: PACK1.NAME_ARRAY
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)
at oracle.sql.ArrayDescriptor.<init>(ArrayDescriptor.java:274)
at oracle.sql.ArrayDescriptor.createDescriptor(ArrayDescriptor.java:127)
at oracle.sql.ArrayDescriptor.createDescriptor(ArrayDescriptor.java:79)
at oracle.jdbc.driver.NamedTypeAccessor.otypeFromName(NamedTypeAccessor.java:83)
at oracle.jdbc.driver.TypeAccessor.initMetadata(TypeAccessor.java:76)
at oracle.jdbc.driver.T4CCallableStatement.allocateAccessor(T4CCallableStatement.java:599)
at oracle.jdbc.driver.OracleCallableStatement.registerOutParameterInternal(OracleCallableStatement.java:201)
at oracle.jdbc.driver.OracleCallableStatement.registerOutParameter(OracleCallableStatement.java:240)
at oracle.jdbc.driver.OracleCallableStatementWrapper.registerOutParameter(OracleCallableStatementWrapper.java:1243)
at com.zaxxer.hikari.pool.HikariProxyCallableStatement.registerOutParameter(HikariProxyCallableStatement.java)
at org.springframework.jdbc.core.CallableStatementCreatorFactory$CallableStatementCreatorImpl.createCallableStatement(CallableStatementCreatorFactory.java:188)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:1090)
at org.springframework.jdbc.core.JdbcTemplate.call(JdbcTemplate.java:1147)
at org.springframework.jdbc.core.simple.AbstractJdbcCall.executeCallInternal(AbstractJdbcCall.java:412)
at org.springframework.jdbc.core.simple.AbstractJdbcCall.doExecute(AbstractJdbcCall.java:372)
at org.springframework.jdbc.core.simple.SimpleJdbcCall.execute(SimpleJdbcCall.java:198)
不幸的是,我无法更改客户端数据库中的任何内容 :( 所以我无法更改声明 TYPE name_array IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER;
,我需要在 java、spring 中构建应用程序启动。有没有办法在 oracle 上不更改程序和包的情况下执行此操作。
我做错了什么?提前致谢。
one of them is a out parameter type
TABLE OF VARCHAR2
你错了,其中一个是 TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER
,它是一个关联数组,你不能使用 JDBC.
见
- 您需要使用
CREATE TYPE
在 SQL 范围内定义数据类型(而不是在包中的 PL/SQL 范围内),因为 JDBC 只能使用 SQL 定义的数据类型。 - 第 1 点的结果是 JDBC 不支持关联数组,因为它们只是 PL/SQL 数据类型,您需要使用集合(不像 C#,它只支持关联数组数组,不支持集合)。因此,您需要从类型中删除
INDEX BY
子句。
在包外声明类型使用:
CREATE TYPE name_array IS TABLE OF VARCHAR2(50);
然后从包中删除关联数组声明并改用新的集合类型。之后可能会有更多的错误需要调试;但它永远不会使用关联数组工作。
unfortunately, I couldn't change anything in client's database :( so I can't change the declaration
TYPE name_array IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER;
and I need to build a application in java, spring boot. Is there some way to do this without change procedure and package on oracle.
您可以尝试通过调用匿名 PL/SQL 块(依次调用程序包)将关联数组转换为 VARRAY
例如 SYS.ODCIVARCHAR2LIST
:
DECLARE
v_name_assoc_array PACK1.NAME_ARRAY;
v_name_list SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST();
v_idx BINARY_INTEGER;
BEGIN
PACK1.proc_filter_and_return_array( :p_name_in, v_name_assoc_array );
v_idx := v_name_assoc_array.FIRST;
WHILE v_idx IS NOT NULL LOOP
v_name_list.EXTEND;
v_name_list(v_name_list.COUNT) := v_name_assoc_array(v_idx);
v_idx := v_name_assoc_array.NEXT(v_idx);
END LOOP;
:p_name_out_array := v_name_list;
END;
/
我来这里是为了展示代码,因为 MTO 已经回答了我。 从 java 调用匿名 PL/SQL 块并获得 return 是最好的解决方案。
终于成功得到下面的代码了:
// declaring a plsql block calling procedure and treating out return parameters
String plSql = " DECLARE "+
" p_name_array PACK1.NAME_ARRAY "+
" p_name_out_array_return SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST(); "+
" v_idx BINARY_INTEGER; "+
" BEGIN "+
" "+
" pack1.proc_filter_and_return_array( p_name_in => ? , p_name_out_array => p_name_array); "+
" "+
" v_idx := p_name_array.first; "+
" WHILE v_idx IS NOT NULL LOOP "+
" p_name_out_array_return.extend; "+
" p_name_out_array_return(idx + 1 ) := p_name_array(idx); "+
" v_idx := p_name_array.next(v_idx); "+
" END LOOP; "+
" "+
" ? := p_name_out_array_return; "+
" END; ";
// calling plsql from jdbcTemplate
jdbcTemplate.execute( new CallableStatementCreator(){
@Override
public CallableStatement createCallableStatement(Connection con) throws SQLException {
CallableStatement cs = con.prepareCall(plSql);
cs.setString(1, "value to first parameter ? in plsql");
// registring out second param p_name_out_array_return used in plsql
cs.registerOutParameter(2, Types.ARRAY , SYS.ODCIVARCHAR2LIST);
return cs;
}
} , new CallableStatementCallback<Object>(){
@Override
public Object doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
cs.execute();
// do something with result out param 2
Arrays.asList((Object[])cs.getArray(2).getArray()).forEach(System.out::println);
return null;
}
} );