Spring调用StoredProcedure.execute()抛出数组越界异常排查
问题根因
你怀疑的空presenters数组、Spring StoredProcedure逻辑缺陷都不是本次异常的诱因。从异常堆栈可以明确:错误发生在序列化pi_booking(对应BOOKING_TYP结构体)参数的阶段,此时Spring的参数校验逻辑已经执行完毕,还未进入pi_presenters数组参数的处理流程,异常是Oracle JDBC驱动在处理结构体序列化时抛出的。
堆栈中明确指向test.mybookingsystem.model.SQLBooking.writeSQL(SQLBooking.java:100),即你自定义的SQLBooking类(实现SQLData接口用于Oracle结构体映射)的writeSQL方法第100行,调用OracleSQLOutput.writeString时触发了数组越界。
常见触发场景
- 你使用的是老版本Oracle JDBC驱动(11.2.0.3及更早版本),该版本驱动的
OracleSQLOutput.writeString方法存在已知缺陷:当传入null值时,内部未做判空处理,直接尝试读取字符串长度初始化输出缓冲区,最终抛出ArrayIndexOutOfBoundsException: 0,而不是正常写入SQL NULL值。 SQLBooking.writeSQL方法中写入属性的顺序、数量和数据库中BOOKING_TYP定义的属性不匹配。Oracle结构体序列化严格要求写入顺序和类型定义顺序一致,BOOKING_TYP定义的属性顺序为booking_id→room_name→start_date→duration,如果少写属性、调换写入顺序,驱动按结构体元数据读取写入值时就会出现索引越界。
修复方案
- 优先修复
SQLBooking.writeSQL方法的null处理逻辑:对所有String类型属性做判空,null值时调用writeNull()而非直接传入writeString,示例代码:
// 错误写法:传入null会触发老版本驱动bug // output.writeString(booking.getStartDate()); // 正确写法 if (booking.getStartDate() == null) { output.writeNull(); } else { output.writeString(booking.getStartDate()); }
- 建议升级Oracle JDBC驱动到11.2.0.4及以上版本,新版本已经修复了
writeString处理null值的缺陷,可自动识别null写入SQL NULL。 - 核对
writeSQL中的属性写入顺序、数量,确保和BOOKING_TYP的数据库定义完全一致,不能缺省、不能调换顺序。 - 补充数组参数的边界处理:在
SQLPersonArray.createTypeValue中增加空数组、null值判断,避免后续处理数组参数时触发异常,同时建议用ArrayDescriptor.createDescriptor获取描述符(复用缓存,性能更好):
@Override protected Object createTypeValue(Connection connection, int sqlType, String typeName) throws SQLException { if (sqlPersonArr == null || sqlPersonArr.length == 0) { return null; } ArrayDescriptor arrayDescriptor = ArrayDescriptor.createDescriptor(typeName, connection); return new oracle.sql.ARRAY(arrayDescriptor, connection, sqlPersonArr); }
- 额外注意:你定义
PERSON_TYP时使用了双引号,Oracle会将其识别为大小写敏感的类型名,如果后续处理presenters参数时报类型不存在错误,需要将参数声明中的类型名改为大写的PERSON_TYP即可。
内容的提问来源于stack exchange,提问作者user2526586
相关产品推荐
相关产品推荐

