使用OJDBC11与MyBatis调用存储过程时遇Invalid Column Type:2000错误
问题分析与解决思路
为什么JAVA_OBJECT(2000)不被Oracle驱动支持作为OUT参数
Oracle JDBC驱动对输入、输出参数的类型处理逻辑不同:
JAVA_OBJECT仅适用于IN参数,用于将Java对象直接映射到SQL类型,但驱动无法反向从数据库的自定义STRUCT类型自动推断如何转换为Java对象,因此不允许将其作为OUT参数的JDBC类型注册。- 当注册OUT参数时,驱动需要明确知道对应的数据库SQL类型(如STRUCT),以及具体的自定义类型名称,才能正确完成从数据库对象到Java对象的转换。
解决步骤
1. 修改MyBatis映射配置,替换OUT参数的JDBC类型
将OUT参数的jdbcType从JAVA_OBJECT改为STRUCT,同时添加typeName属性指定Oracle自定义类型的全限定名(包含SCHEMA):
<mapper namespace="MyNamespace"> <insert id="mycall" parameterType="map" statementType="CALLABLE"> {CALL MyPackage.MyProcedure( #{x, mode=IN, jdbcType=JAVA_OBJECT, typeHandler=com.learning.MyHandler1}, #{y, mode=OUT, jdbcType=STRUCT, typeName="YOUR_SCHEMA.CUSTOMTYPE2", typeHandler=com.learning.MyHandler2} )} </insert> </mapper>
2. 调整自定义TypeHandler的OUT参数处理逻辑
确保MyHandler2正确实现从Oracle STRUCT到Java对象的转换,关键是重写getNullableResult方法:
public class MyHandler2 extends BaseTypeHandler<CustomType2> { @Override public void setNonNullParameter(PreparedStatement ps, int i, CustomType2 parameter, JdbcType jdbcType) throws SQLException { // OUT参数无需实现此方法 } @Override public CustomType2 getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { // 获取Oracle STRUCT对象 Struct struct = (Struct) cs.getObject(columnIndex); if (struct == null) { return null; } // 将STRUCT的属性映射到Java对象 Object[] attributes = struct.getAttributes(); CustomType2 result = new CustomType2(); result.setField1((String) attributes[0]); result.setField2((Integer) attributes[1]); // 补充其他字段的映射逻辑 return result; } // 按需实现其他getNullableResult重载方法 }
3. 可选:使用Oracle特定API优化转换
如果需要更精细的控制,可以直接强转为OracleCallableStatement调用Oracle专属方法:
OracleCallableStatement ocs = (OracleCallableStatement) cs; STRUCT oracleStruct = ocs.getSTRUCT(columnIndex); // 后续映射逻辑同上
核心原理
通过指定jdbcType=STRUCT和typeName,MyBatis会调用Oracle驱动的registerOutParameter(int parameterIndex, int sqlType, String typeName)重载方法,驱动将根据指定的自定义类型名正确解析数据库返回的STRUCT,避免触发Invalid column type: 2000错误。
内容的提问来源于stack exchange,提问作者DBreaker
相关产品推荐
相关产品推荐

