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

迁移至MySQL Connector/J 8.x时CallableStatement报OUT参数异常

MySQL Connector/J 8.x迁移后CallableStatement报错:Parameter number 2 is not an OUT parameter

将代码从MySQL Connector/J 5.1.49迁移至8.1.0(所有8.*版本均出现此问题)时,原本正常运行的CallableStatement代码抛出如下异常:

SQLException: Parameter number 2 is not an OUT parameter

对应的存储过程

CREATE PROCEDURE get_system_setting(IN p_setting VARCHAR(200), OUT message_out VARCHAR(200))
BEGIN
  SELECT setting, value, message_out FROM SYSTEM_SETTING WHERE setting = p_setting;
END;

示例Java代码

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

public class CallableTest {

  /* MySQL Connector/J 8.1.0 (所有8.*版本均失败) */
  private static final String DB_DRIVER_CLASS = "com.mysql.cj.jdbc.Driver";

  /* MySQL Connector/J 5.1.49 - 注释上一行,取消注释下一行以使用v5 */
  //private static final String DB_DRIVER_CLASS = "com.mysql.jdbc.Driver";

  private static final String DB_URL = "jdbc:mysql://localhost:3306/DB_NAME?useSSL=false";
  private static final String DB_USERNAME = "user";
  private static final String DB_PASSWORD = "password";

  public static Connection getConnection() {
    Connection conn = null;
    try {
      Class.forName(DB_DRIVER_CLASS);
      conn = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    } catch (SQLException e) {
     e.printStackTrace();
    }
    return conn;
  }

  public static void main(String[] args) {
    Connection conn = null;
    CallableStatement cs = null;

    try {
      conn = CallableTest.getConnection();
      cs = conn.prepareCall("{call DB_NAME.get_system_setting(?, ?)}");
      cs.setString(1, "CONNECT_TIMEOUT");
      cs.registerOutParameter(2, Types.VARCHAR); // <-- 此处报错

      cs.execute();
      String message = cs.getString(2);
      if(message != null) {
        System.out.printf("out parameter: %s\n",  message);
      } else {
        ResultSet rs = cs.getResultSet();
        boolean rowsReturned = ((rs != null) && rs.next());
        if (rowsReturned) {
          String setting = rs.getString("setting");
          String value = rs.getString("value");
          System.out.printf("setting: %s, value: %s\n", setting, value);
        }
     }
   } catch(Exception e) {
     e.printStackTrace();
   } finally {
     try {
       cs.close();
       conn.close();
     } catch (SQLException e) {
       e.printStackTrace();
     }
   }
  }
}

8.x版本执行报错信息

java -cp .:mysql-connector-j-8.1.0.jar CallableTest

java.sql.SQLException: Parameter number 2 is not an OUT parameter
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:130)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:98)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:90)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:64)
    at com.mysql.cj.jdbc.CallableStatement.checkIsOutputParam(CallableStatement.java:645)
    at com.mysql.cj.jdbc.CallableStatement.registerOutParameter(CallableStatement.java:1717)
    at com.mysql.cj.jdbc.CallableStatement.registerOutParameter(CallableStatement.java:1725)
    at CallableTest.main(CallableTest.java:49)

5.1.49版本正常运行结果

java -cp .:mysql-connector-java-5.1.49.jar CallableTest

setting: CONNECT_TIMEOUT, value: 10000

已尝试使用命名参数和索引参数,查阅Connector/J文档未找到CallableStatement相关版本变更说明。

环境信息

  • Connector/J: 8.1.0
  • MySQL: 8.0.33
  • Java: 1.8.0_331

问题原因及解决方案

问题根源是存储过程的实现:SELECT语句直接引用了未赋值的OUT参数message_out,MySQL Connector/J 8.x对存储过程参数的校验逻辑更严格,判定该OUT参数未被正确使用(无赋值操作),因此拒绝注册它为输出参数。

修复方案1:先给OUT参数赋值再查询

修改存储过程,先为message_out赋值(可根据业务逻辑设置具体值),再执行查询:

CREATE PROCEDURE get_system_setting(IN p_setting VARCHAR(200), OUT message_out VARCHAR(200))
BEGIN
  -- 先为OUT参数赋值,示例设为空字符串
  SET message_out = '';
  SELECT setting, value, message_out FROM SYSTEM_SETTING WHERE setting = p_setting;
END;

修复方案2:移除SELECT语句中的OUT参数引用

如果无需在结果集中返回message_out,直接去掉SELECT里的该参数,并根据业务逻辑为其赋值:

CREATE PROCEDURE get_system_setting(IN p_setting VARCHAR(200), OUT message_out VARCHAR(200))
BEGIN
  SELECT setting, value FROM SYSTEM_SETTING WHERE setting = p_setting;
  -- 根据查询结果给OUT参数赋值
  IF ROW_COUNT() = 0 THEN
    SET message_out = '未找到对应配置项';
  ELSE
    SET message_out = '查询成功';
  END IF;
END;

修复方案3:临时兼容旧版校验逻辑(不推荐)

若暂时无法修改存储过程,可在JDBC URL中添加useLegacyDatetimeCode=true,强制Connector/J使用旧版参数校验逻辑,但这只是临时 workaround,可能引发其他兼容性问题:

private static final String DB_URL = "jdbc:mysql://localhost:3306/DB_NAME?useSSL=false&useLegacyDatetimeCode=true";

内容的提问来源于stack exchange,提问作者Boyan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 14:00:38