我尝试使用一个具有PL / SQL记录类型的IN参数调用Oracle过程。
我写了以下代码:
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate).withProcedureName("P_INSERTCLIENT");
jdbcCall.declareParameters(new SqlParameter("V_CLIENT_RECORD", OracleTypes.ARRAY));
MapSqlParameterSource in = new MapSqlParameterSource().addValue("V_CLIENT_RECORD", client);
jdbcCall.execute(in);
当我执行代码时,我收到以下错误:
java.sql.SQLException: Fail to convert to internal representation: com.my.test.resource.model.Client@5df613a2
我想知道是否可以使用定义为PL / SQL Record的IN参数调用过程以及如何操作?
答案 0 :(得分:0)
我认为你不能使用OracleTypes.Array,因为它是一个记录。
https://docs.oracle.com/cd/E11882_01/java.112/e16548/apxref.htm#JJDBC28928
PL / SQL TABLE,BOOLEAN和RECORD类型
Oracle JDBC驱动程序支持调用参数或返回PL / SQL RECORD,BOOLEAN或具有非标量元素类型的表的值是不可行的。但是,Oracle JDBC驱动程序支持标量元素类型的PL / SQL索引表。
另见:
“按表格访问PL / SQL索引”
作为PL / SQL RECORD,BOOLEAN或非标量表类型的变通方法,创建容器过程,以JDBC支持的类型处理数据。例如,要包装使用PL / SQL boolean的存储过程,请创建一个存储过程,该过程从JDBC获取字符或数字,并将其作为BOOLEAN传递给原始过程,或者对于输出参数,从原始过程接受BOOLEAN参数。过程并将其作为CHAR或NUMBER传递给JDBC。同样,要包装使用PL / SQL记录的存储过程,请创建一个存储过程来处理其各个组件(如CHAR和NUMBER)或结构化对象类型中的记录。要包装使用PL / SQL表的存储过程,请将数据分解为组件或使用Oracle集合类型。
答案 1 :(得分:0)
不幸的是,Oracle JDBC驱动程序无法提供对RECORD
或IN
参数的PL / SQL OUT
类型的访问。
但您可以直接在JDBC(或Spring JDBC)中使用匿名PL / SQL块来解决此限制:
DECLARE
rec MY_PACKAGE.MY_RECORD;
BEGIN
-- Replace these by your actual record attributes:
rec.first_name := ?;
rec.last_name := ?;
...
p_insertclient(rec);
END;
这适用于单个过程调用。
如果您经常执行上述操作,则值得生成存根,自动生成匿名PL / SQL字符串,而不是一直手动编写。您可以使用以下查询自动发现架构中的所有PL / SQL RECORD
类型:
SELECT
x.TYPE_OWNER, x.TYPE_NAME, x.TYPE_SUBNAME, a.ARGUMENT_NAME ATTR_NAME,
a.SEQUENCE ATTR_NO, a.TYPE_OWNER ATTR_TYPE_OWNER,
nvl2(a.TYPE_SUBNAME, a.TYPE_NAME, NULL) package_name,
COALESCE(a.TYPE_SUBNAME, a.TYPE_NAME, a.DATA_TYPE) ATTR_TYPE_NAME,
a.DATA_LENGTH LENGTH, a.DATA_PRECISION PRECISION, a.DATA_SCALE SCALE
FROM SYS.ALL_ARGUMENTS a
JOIN (
SELECT
a.TYPE_OWNER, a.TYPE_NAME, a.TYPE_SUBNAME,
MIN(a.OWNER) KEEP (DENSE_RANK FIRST ORDER BY a.OWNER ASC, a.PACKAGE_NAME ASC, a.SUBPROGRAM_ID ASC, a.SEQUENCE ASC) OWNER,
MIN(a.PACKAGE_NAME) KEEP (DENSE_RANK FIRST ORDER BY a.OWNER ASC, a.PACKAGE_NAME ASC, a.SUBPROGRAM_ID ASC, a.SEQUENCE ASC) PACKAGE_NAME,
MIN(a.SUBPROGRAM_ID) KEEP (DENSE_RANK FIRST ORDER BY a.OWNER ASC, a.PACKAGE_NAME ASC, a.SUBPROGRAM_ID ASC, a.SEQUENCE ASC) SUBPROGRAM_ID,
MIN(a.SEQUENCE) KEEP (DENSE_RANK FIRST ORDER BY a.OWNER ASC, a.PACKAGE_NAME ASC, a.SUBPROGRAM_ID ASC, a.SEQUENCE ASC) SEQUENCE,
MIN(next_sibling) KEEP (DENSE_RANK FIRST ORDER BY a.OWNER ASC, a.PACKAGE_NAME ASC, a.SUBPROGRAM_ID ASC, a.SEQUENCE ASC) next_sibling,
MIN(a.DATA_LEVEL) KEEP (DENSE_RANK FIRST ORDER BY a.OWNER ASC, a.PACKAGE_NAME ASC, a.SUBPROGRAM_ID ASC, a.SEQUENCE ASC) DATA_LEVEL
FROM (
SELECT
lead(a.SEQUENCE, 1, a.SEQUENCE) OVER (
PARTITION BY a.OWNER, a.PACKAGE_NAME, a.SUBPROGRAM_ID, a.DATA_LEVEL
ORDER BY a.SEQUENCE ASC
) next_sibling,
a.TYPE_OWNER, a.TYPE_NAME, a.TYPE_SUBNAME, a.OWNER, a.PACKAGE_NAME,
a.SUBPROGRAM_ID, a.SEQUENCE, a.DATA_LEVEL, a.DATA_TYPE
FROM SYS.ALL_ARGUMENTS a
WHERE a.OWNER IN ('MY_SCHEMA') -- Possibly replace schema here
) a
WHERE (a.TYPE_OWNER IN ('MY_SCHEMA') -- Possibly replace schema here
AND a.OWNER IN ('MY_SCHEMA') -- Possibly replace schema here
AND a.DATA_TYPE = 'PL/SQL RECORD')
GROUP BY a.TYPE_OWNER, a.TYPE_NAME, a.TYPE_SUBNAME
) x
ON ((a.OWNER, a.PACKAGE_NAME, a.SUBPROGRAM_ID) = ((x.OWNER, x.PACKAGE_NAME, x.SUBPROGRAM_ID))
AND a.SEQUENCE BETWEEN x.SEQUENCE AND next_sibling
AND a.DATA_LEVEL = (x.DATA_LEVEL + 1))
ORDER BY x.TYPE_OWNER ASC, x.TYPE_NAME ASC, x.TYPE_SUBNAME ASC, a.SEQUENCE ASC
See more details about this technique in this blog post (from which the query was taken)