Vertx调用Oracle存储过程INOUT参数传NULL报错问题
Vertx JDBC调用Oracle存储过程INOUT参数传NULL报类型冲突的解决方法
环境信息
- Vertx版本:4.5.3
- Oracle JDBC驱动版本:19.18.0.0
问题描述
调用Oracle存储过程set_privileges时,给INOUT类型的p_privilege_id参数传递NULL值,触发java.sql.SQLException: Parameter Type Conflict异常。临时方案是将NULL替换为0,再在存储过程内部转成NULL,但需要更优的原生解决方案。尝试过SqlOutParam.INOUT(NullValue.Integer,"NUMERIC"),问题依然存在。
复现代码
连接池配置
Pool pool = JDBCPool.pool(vertx, new JDBCConnectOptions() .setJdbcUrl("jdbc:oracle:thin:@//localhost:1521/instance") .setUser("tiger") .setPassword("scott"), new PoolOptions() .setMaxSize(16) .setName("pool-name") );
存储过程调用代码
String sql = "{call set_privileges(p_privilege_id => ?, p_user_id=> ?, p_priv_group => ?, p_sub_priv_group => ?)}"; pool .preparedQuery(sql) .execute(Tuple.of( SqlOutParam.INOUT(null,"NUMERIC"), // 触发异常的INOUT参数 1001, 1, null)) .onFailure(e -> log.error(e)) .onSuccess(rows -> log.info(rows.rowCount()));
异常栈信息
2024-03-02 16:17:04,586 ERROR [vert.x-worker-thread-19] sg.ae.ag.co.se.im.OracleExceptionStack Oracle Fatal Error java.sql.SQLException: Parameter Type Conflict at oracle.jdbc.driver.OraclePreparedStatement.processCompletedBindRow(OraclePreparedStatement.java:2395) ~[ojdbc10-19.18.0.0.jar:19.18.0.0.0] at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3657) ~[ojdbc10-19.18.0.0.jar:19.18.0.0.0] at oracle.jdbc.driver.T4CCallableStatement.executeInternal(T4CCallableStatement.java:1358) ~[ojdbc10-19.18.0.0.jar:19.18.0.0.0] at oracle.jdbc.driver.OraclePreparedStatement.execute(OraclePreparedStatement.java:3778) ~[ojdbc10-19.18.0.0.jar:19.18.0.0.0] at oracle.jdbc.driver.OracleCallableStatement.execute(OracleCallableStatement.java:4251) ~[ojdbc10-19.18.0.0.jar:19.18.0.0.0] at oracle.jdbc.driver.OraclePreparedStatementWrapper.execute(OraclePreparedStatementWrapper.java:1081) ~[ojdbc10-19.18.0.0.jar:19.18.0.0.0] at com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44) ~[HikariCP-5.0.1.jar:?] at com.zaxxer.hikari.pool.HikariProxyCallableStatement.execute(HikariProxyCallableStatement.java) ~[HikariCP-5.0.1.jar:?] at io.vertx.jdbcclient.impl.actions.JDBCPreparedQuery.execute(JDBCPreparedQuery.java:64) ~[vertx-jdbc-client-4.5.3.jar:4.5.3] at io.vertx.jdbcclient.impl.actions.JDBCPreparedQuery.execute(JDBCPreparedQuery.java:44) ~[vertx-jdbc-client-4.5.3.jar:4.5.3] at io.vertx.ext.jdbc.impl.JDBCConnectionImpl.lambda$schedule$3(JDBCConnectionImpl.java:226) ~[vertx-jdbc-client-4.5.3.jar:4.5.3] at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$1(ContextImpl.java:190) ~[vertx-core-4.5.3.jar:4.5.3]
正确解决方案
方法1:直接操作CallableStatement,显式绑定NULL与参数类型
绕开Vertx的Tuple和SqlOutParam封装,使用JDBC原生API确保驱动能正确识别参数类型:
pool.getConnection().onSuccess(conn -> { conn.prepareCall(sql).onSuccess(callable -> { // 处理INOUT参数:先设为NULL,再注册输出类型 callable.setNull(1, java.sql.Types.NUMERIC); callable.registerOutParameter(1, java.sql.Types.NUMERIC); // 绑定其他参数 callable.setInt(2, 1001); callable.setInt(3, 1); callable.setNull(4, java.sql.Types.NUMERIC); callable.execute().onSuccess(rows -> { log.info(rows.rowCount()); // 获取输出参数值(如果需要) Integer privId = callable.wasNull() ? null : callable.getInt(1); callable.close(); conn.close(); }).onFailure(e -> { log.error(e); callable.close(); conn.close(); }); }).onFailure(e -> { log.error(e); conn.close(); }); });
方法2:使用Oracle专属类型常量提升兼容性
如果依赖Oracle JDBC驱动的专属API,可以用OracleTypes.NUMBER替代java.sql.Types.NUMERIC,类型匹配更精准:
import oracle.jdbc.OracleTypes; // ... callable.setNull(1, OracleTypes.NUMBER); callable.registerOutParameter(1, OracleTypes.NUMBER);
方法3:升级Vertx JDBC客户端版本
问题根源可能是Vertx 4.5.3的SqlOutParam对NULL值的类型绑定处理有缺陷,尝试升级到4.5.4及以上版本,看是否修复了该参数绑定bug。
总结
传递INOUT类型的NULL参数给Oracle存储过程时,直接使用JDBC原生的setNull+registerOutParameter是最可靠的方案,能避免Vertx封装层带来的类型识别问题。若坚持使用Vertx的简化API,优先考虑升级版本或提交issue反馈问题。
内容的提问来源于stack exchange,提问作者Nik
相关产品推荐
相关产品推荐

