调用存储过程触发java.lang.NegativeArraySizeException:原因与修复
Spring Data JPA调用存储过程触发NegativeArraySizeException问题排查
问题场景
我使用Spring Data JPA调用Oracle存储过程,代码实现如下:
@Query(value = "DECLARE " + "p_transaction_header schema.transaction_header_rec := NULL; " + "x_voucher_id NUMBER; " + "x_return_status VARCHAR2(200); " + "x_rejection_reason VARCHAR2(4000); " + "BEGIN " + "p_transaction_header.source :=:source; " + "p_transaction_header.total_amount :=:totalAmount; " + "p_transaction_header.total_records :=:totalRecords; " + "p_transaction_header.external_key_identifier :=:externalKeyIdentifier; " + "schema.open_transaction(p_transaction_header, x_voucher_id, x_return_status, x_rejection_reason); " + "END", nativeQuery = true) OpenTransactionBo openTransaction(@Param("source") String source, @Param("totalAmount") BigDecimal totalAmount, @Param("totalRecords") BigDecimal totalRecords, @Param("externalKeyIdentifier") String externalKeyIdentifier);
执行时触发如下异常:
java.lang.NegativeArraySizeException
异常原因
- 返回值映射不匹配:当前方法指定
OpenTransactionBo作为返回值,但手动编写的PL/SQL块没有返回任何结果集,也没有将存储过程的输出参数绑定到返回实体。Spring Data JPA解析返回结果时,无法识别有效数据长度,导致内部数组初始化时出现负数大小,抛出该异常。 - 输出参数未处理:存储过程的
x_voucher_id、x_return_status、x_rejection_reason都是输出参数,但代码没有声明接收这些参数的方式,JPA无法正确绑定输出参数,触发解析逻辑错误。 - 自定义类型支持问题:使用了Oracle自定义记录类型
transaction_header_rec,Spring Data JPA对这类自定义类型的原生查询支持有限,参数绑定或结果解析时易出现内部逻辑错误,引发数组大小异常。
修复方案
方案1:使用@Procedure注解替代原生@Query
直接通过Spring Data JPA的@Procedure注解绑定存储过程,让框架自动处理参数映射和输出参数绑定,避免手动编写PL/SQL块:
@Procedure(procedureName = "schema.open_transaction") void openTransaction( @Param("p_transaction_header") TransactionHeaderRec transactionHeader, @Param("x_voucher_id") Out<BigDecimal> voucherId, @Param("x_return_status") Out<String> returnStatus, @Param("x_rejection_reason") Out<String> rejectionReason );
需要先定义对应自定义记录类型的Java类,并用Hibernate的@Struct注解映射:
@Struct(name = "TRANSACTION_HEADER_REC", schema = "SCHEMA") public class TransactionHeaderRec { private String source; private BigDecimal totalAmount; private BigDecimal totalRecords; private String externalKeyIdentifier; // 全参构造器、getter/setter方法 }
调用后可通过Out参数获取返回值,再手动封装到OpenTransactionBo中。
方案2:修改原生查询,明确返回结果
如果坚持使用@Query,需要修改PL/SQL块,将输出参数通过SELECT语句返回,同时调整方法返回类型为Object[]:
@Query(value = "DECLARE " + "p_transaction_header schema.transaction_header_rec := NULL; " + "x_voucher_id NUMBER; " + "x_return_status VARCHAR2(200); " + "x_rejection_reason VARCHAR2(4000); " + "BEGIN " + "p_transaction_header.source :=:source; " + "p_transaction_header.total_amount :=:totalAmount; " + "p_transaction_header.total_records :=:totalRecords; " + "p_transaction_header.external_key_identifier :=:externalKeyIdentifier; " + "schema.open_transaction(p_transaction_header, x_voucher_id, x_return_status, x_rejection_reason); " + "SELECT x_voucher_id, x_return_status, x_rejection_reason FROM DUAL; " + "END", nativeQuery = true) Object[] openTransaction( @Param("source") String source, @Param("totalAmount") BigDecimal totalAmount, @Param("totalRecords") BigDecimal totalRecords, @Param("externalKeyIdentifier") String externalKeyIdentifier );
调用后将Object[]中的元素手动映射到OpenTransactionBo对象的对应字段。
方案3:升级依赖版本
部分旧版本的Hibernate或Oracle JDBC驱动对自定义类型的处理存在bug,升级到最新稳定版(如Hibernate 5.6+、Oracle JDBC 19c及以上),可以修复这类底层解析异常。
内容的提问来源于stack exchange,提问作者Gurpreet Kaur
相关产品推荐
相关产品推荐

