从存储过程返回的ResultSet获取列名时遇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

