ojdbc8 19.3下CallableStatement命名绑定调用函数报PLS-00306错误
问题背景
Java 8项目中将Oracle JDBC驱动从ojdbc6升级至ojdbc8 19.3.0.0版本后,调用带返回值的PL/SQL函数时抛出如下异常:
Caused by: java.sql.SQLException: ORA-06550: line 1, column 18: PLS-00306: wrong number or types of arguments in call to 'MAKEWORK' ORA-06550: line 1, column 7: PL/SQL: Statement ignored
触发场景
执行以下命名绑定方式的函数调用代码时触发异常:
try (CallableStatement cs = con.prepareCall("{call :result := CORE.PKG.makeWork(arg1=>:arg1, arg2=>:arg2)}")) { cs.setString("arg1", javaArg1); cs.setInt("arg2", javaArg2); cs.registerOutParameter("result", Types.ARRAY, CommonTypeNameConstant.T_TABLE_OF_NUMBERS_TYPE); cs.execute(); Array result = (Array) cs.getObject("result"); }
该问题仅存在于函数调用场景:如果CORE.PKG.makeWork是存储过程,相同的命名绑定写法可正常运行:
try (CallableStatement cs = con.prepareCall("{call CORE.PKG.makeWork(arg1=>:arg1, arg2=>:arg2)}")) { cs.setString("arg1", javaArg1); cs.setInt("arg2", javaArg2); cs.execute(); }
已验证的临时方案
将命名绑定改为顺序绑定可解决问题,但改造成本较高:
try (CallableStatement cs = con.prepareCall("{? = call CORE.PKG.makeWork(arg1=>?, arg2=>?)}")) { cs.registerOutParameter(1, Types.ARRAY, CommonTypeNameConstant.T_TABLE_OF_NUMBERS_TYPE); cs.setObject(2, javaArg1); cs.setObject(3, javaArg2); cs.execute(); Array result = (Array) cs.getObject(1); }
该方案存在两个明显弊端:
- 顺序绑定容易出现参数顺序编写错误,提升代码bug率
- 项目中存在大量存量命名绑定脚本,全量改写人力成本高
问题根因
出现该问题的核心原因是ojdbc8 19.3.0.0版本的参数解析逻辑做了收紧调整:
- JDBC规范中定义的存储过程调用标准语法为
{call 过程名(参数列表)},函数调用标准语法为{? = call 函数名(参数列表)}。之前使用的{call :result := 函数名(...)}是Oracle旧驱动兼容的非标准PL/SQL块写法,不属于JDBC标准调用语法范畴。 - ojdbc6及更早版本对非标准写法做了宽松兼容:会自动识别赋值符
:=左侧的:result为命名绑定的输出参数,和参数列表内的命名占位符统一做名称映射,因此代码可正常运行。 - ojdbc8 19.3.0.0版本重构了
CallableStatement的参数解析逻辑,默认仅在JDBC标准调用语法下支持命名参数绑定:当检测到非标准的{call ... := ...}写法时,驱动会把大括号内的全部内容当做普通匿名PL/SQL块处理,不会自动识别块内的:result、:arg1这类命名占位符做参数映射,最终传给数据库的PL/SQL块中占位符未被正确绑定参数类型,数据库侧就会抛出PLS-00306参数数量/类型不匹配的错误。 - 存储过程调用场景下使用的是
{call 过程名(...)}的JDBC标准语法,驱动的命名参数解析逻辑可正常生效,因此不会报错。
低改造成本兼容方案
不需要全量改写为顺序绑定,可选择以下两种方案保留命名绑定写法:
- 方案一:开启驱动兼容开关。在JDBC连接配置中添加连接属性
oracle.jdbc.plsqlNamedParameters=true,开启后驱动会恢复ojdbc6版本对非标准语法、匿名PL/SQL块下的命名参数解析逻辑,原有业务代码不需要做任何改动即可正常运行。 - 方案二:微调SQL为标准语法。将所有函数调用的SQL从非标准的
{call :返回值变量 := 函数名(参数列表)}调整为JDBC标准格式{? = call 函数名(参数列表)},参数列表内部的命名绑定写法(arg1=>:arg1、arg2=>:arg2)完全不需要改动,仅需将返回值的注册、取值逻辑从原命名参数改为获取第1位参数即可,改造量远低于全量改为顺序绑定。
内容的提问来源于stack exchange,提问作者Robert Ishmukhametov
相关产品推荐
相关产品推荐

