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

从存储过程返回的ResultSet获取列名时遇ORA-01403错误

解决从Oracle存储过程返回的STRUCT元数据中获取列名时的ORA-01403错误

你碰到的这个ORA-01403错误,本质是虽然ResultSetMetaData.getColumnCount()返回了15,但调用getColumnName()时驱动找不到对应的列名数据。这是Oracle JDBC处理自定义对象类型元数据时的一个常见坑,咱们来拆解问题并给出几种解决方案:

问题原因分析

你的Java代码是从STRUCT.getDescriptor().getMetaData()获取元数据,但Oracle JDBC驱动对这种方式返回的ResultSetMetaData支持有限——它能正确返回列的数量,但不会填充实际的列名信息,导致调用getColumnName()时触发"No Data Found"异常。

解决方案一:改用Oracle专属API获取属性名

Oracle的STRUCT实现类(oracle.sql.STRUCT)提供了直接获取属性名的方法,绕过不可靠的ResultSetMetaData,代码修改如下:

while (rs.next()) {
 STRUCT structure = ((STRUCT) rs.getObject(2));
 if(i == 0) {
  // 强转成Oracle专属的STRUCT实现
  oracle.sql.STRUCT oracleStruct = (oracle.sql.STRUCT) structure;
  // 直接获取属性名数组
  String[] attrNames = oracleStruct.getDescriptor().getAttributeNames();
  int numAttrs = attrNames.length;
  System.out.println("descriptor length = " + numAttrs);
  System.out.println("Number of columns = " + numAttrs);
  for (int j = 0; j < numAttrs; j++) {
   String name = attrNames[j];
   System.out.println("Column Name " + (j+1) + " = " + name);
  }
 }
 // 后续处理逻辑不变...
 i++;
}

这种方法直接从StructDescriptor中提取预定义的属性名,无需依赖ResultSetMetaData,能彻底解决ORA-01403问题。

解决方案二:改用REF CURSOR返回结果(推荐)

如果业务允许,修改存储过程返回SYS_REFCURSOR而非自定义对象数组,这是Oracle存储过程返回结果集的标准方式,Java端获取元数据会更简单可靠:

修改后的存储过程代码

PROCEDURE myprocedure (param1 IN TYPE_TABLEAU_CHAINES, param2 IN NUMBER, param3 OUT SYS_REFCURSOR) AS
BEGIN
 -- 直接打开游标返回查询结果
 OPEN param3 FOR 
 SELECT name1, name2, name3, name4, name5, name6, name7, name8, name9, name10, 
        name11, name12, name13, name14, name15 
 FROM TSIGGRC WHERE name22 = 'VALUE';
END myProcedure;

对应的Java代码修改

cstmt = c.prepareCall("CALL MyProcedure(?,?,?)");
ArrayDescriptor descriptionArrayString = ArrayDescriptor.createDescriptor("TYPE_TABLEAU_CHAINES", c);
ARRAY arrayNumfics = new ARRAY(descriptionArrayString, c, numfics.toArray());
cstmt.setArray(1, arrayNumfics);
cstmt.setDouble(2, offreId);
// 注册OUT参数为CURSOR类型
cstmt.registerOutParameter(3, java.sql.Types.CURSOR);
cstmt.execute();
// 获取游标对应的ResultSet
ResultSet rs = (ResultSet) cstmt.getObject(3);
// 直接从ResultSet获取元数据,完全可靠
ResultSetMetaData metadata = rs.getMetaData();
int columnCount = metadata.getColumnCount();
System.out.println("Number of columns = " + columnCount);
for (int j = 1; j <= columnCount; j++) {
 String name = metadata.getColumnName(j);
 System.out.println("Column Name " + j + " = " + name);
}
// 遍历结果集处理数据
while (rs.next()) {
 // 直接通过列名获取数据,比如rs.getString("name1")
 System.out.println("name1 = " + rs.getString("name1"));
 System.out.println("name2 = " + rs.getString("name2"));
 // ...其他列处理
}

这种方式不仅解决了元数据获取的问题,还简化了Java端的结果处理逻辑,是最推荐的方案。

解决方案三:通过数据库元数据查询自定义类型属性

如果必须保留自定义对象数组的返回方式,也可以直接从数据库元数据中查询TYPE_MYOBJECT的属性名:

if(i == 0) {
 DatabaseMetaData dbMeta = c.getMetaData();
 // 查询自定义类型的属性信息
 ResultSet attrRs = dbMeta.getAttributes(
  null, // 用户名,若指定需大写
  null, // 模式名,若指定需大写
  "TYPE_MYOBJECT", // 自定义类型名,必须大写
  null // 属性名过滤
 );
 while (attrRs.next()) {
  String attrName = attrRs.getString("ATTR_NAME");
  int attrIndex = attrRs.getInt("ATTR_POSITION");
  System.out.println("Column Name " + attrIndex + " = " + attrName);
 }
 attrRs.close();
}

注意这里的类型名必须和数据库中定义的一致(Oracle默认大写),否则会查询不到结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 19:48:13