You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 03:57:44