必须在execute()前调用registerOutParameter,参数索引从1开始,类型用java.sql.Types常量;漏注册、顺序错、类型不匹配或取值时机不当均导致SQLException。
CallableStatement.registerOutParameter 什么时候必须调用
不注册就直接
,运行时必抛
或
—— 这不是驱动 bug,是 jdbc 规范强制要求。存储过程的 out/inout 参数在执行前必须显式声明类型,jdbc 驱动靠这个才知道底层怎么分配缓冲区、怎么转换字节流。
常见错误场景:只对 IN 参数设值,漏掉 OUT;或把
放在
之后;或参数序号从 0 开始(实际从 1 开始)。
必须在
或
之前调用
参数索引从
开始,和 SQL 中
出现顺序严格对应
类型要用
常量,比如
、
,别传字符串或数据库原生类型名
如果存储过程有多个 OUT 参数,每个都得单独注册,不能跳过中间某个
Oracle 和 MySQL 的 OUT 参数类型写法差异
Oracle 存储过程常用
、
、
,MySQL 多用
、
、
,但 JDBC 层统一走
。问题出在 Oracle 的
映射上:如果过程返回的是小数(比如
),注册成
会截断;必须用
并配合
参数。
Oracle
→ 优先选
,带精度:
Oracle
/ MySQL
→ 统一用
,别用
(会丢时间部分)
MySQL 的
或大字段 → 注册为
或
,否则可能报
Oracle 的
→ 类型必须是
,取值时用
获取 OUT 参数值的时机和顺序
必须在
成功返回后、且在关闭
前调用
。一旦
,所有 OUT 值丢失。另外,不能按“SQL 里写的顺序”乱取——必须严格按
时的索引位置取,哪怕中间有 IN 参数。
执行完
后立刻取值,别等 ResultSet 处理完(除非是 REF CURSOR)
取值类型要和注册类型兼容:注册
就用
,别用
如果存储过程没给某个 OUT 参数赋值(比如 PL/SQL 里忘了
),取出来是
(Java null),不是 0 或空字符串
对
,MySQL 8.0+ 支持,老版本得用
模拟,取值用
判断 0/1
CallableStatement 关闭前必须检查 SQLException
很多人以为只要
没抛异常,OUT 参数就一定拿到了。错。某些数据库(尤其是 Oracle)会在获取 OUT 值时才真正校验类型匹配或触发隐式转换失败,这时抛出的异常和执行阶段无关,容易被忽略。
在
前,所有
调用都要包 try-catch,或确保上层能捕获
典型错误信息:
,往往是因为注册类型太窄,而过程返回了超长字符串或大数值
别依赖
判断是否为空——它只对刚调用的
有效,且仅当该字段数据库值为 NULL 时才返回 true
批量调用多个存储过程时,每个
的生命周期要独立管理,别复用同一实例去注册不同参数结构的过程
事情说清了就结束。最常出问题的地方不在语法,而在类型注册和取值之间那几毫秒的时序与类型契约——驱动不会替你猜意图,它只认你写死的
。
getxxx()sqlexception: invalid column indexinvalid operation for a result set of type forward_onlyregisterOutParameterexecute()registerOutParameterexecute()executeUpdate()1?java.sql.TypesTypes.VARCHARTypes.INTEGERNUMBERVARCHAR2DATEINTTEXTDATETIMEjava.sql.TypesNUMBERNUMBER(5,2)Types.INTEGERTypes.DECIMALscaleNUMBERTypes.DECIMALcs.registerOutParameter(2, Types.DECIMAL, 10)DATEDATETIMETypes.TIMESTAMPTypes.DATETEXTTypes.LONGVARCHARTypes.CLOBData truncationREF CURSORTypes.OTHERgetResultSet()execute()CallableStatementgetXXX()close()registerOutParametercs.execute()Types.DECIMALgetBigDecimal(2)getInt(2):=nullTypes.BOOLEANTINYINTgetByte()execute()cs.close()getXXX()ORA-06502: PL/SQL: numeric or value errorcs.wasNull()getXXX()CallableStatementregisterOutParameter