oracle存储过程必须用sys_refcursor作为out参数返回结果集,因oracle不支持直接select返回;jpa调用时须注册parametermode.ref_cursor且类型为void.class,仅支持一个ref_cursor输出,推荐用entitymanager原生api而非@procedure注解。

Oracle存储过程必须用sys_refcursor返回结果集
Oracle里没法像MySQL那样直接SELECT * FROM ...让JPA自动映射成List,必须显式声明OUT sys_refcursor参数。如果存储过程只用SELECT语句但没配游标,getResultList()会返回空或抛IllegalStateException。
常见错误现象:java.lang.IllegalStateException: Query does not contain parameter named [xxx] 或 org.hibernate.HibernateException: Could not extract result set,基本都是因为没定义REF_CURSOR参数或类型注册错。
-
registerStoredProcedureParameter("cur", Void.class, ParameterMode.REF_CURSOR)—— 注意类型必须是Void.class,不是ResultSet.class或Object.class - Oracle包内过程需带前缀,比如
pkg_name.proc_name,不能只写proc_name - 一个存储过程里最多只支持一个
REF_CURSOR输出参数,多于一个会报Multiple ref-cursors not supported
@Procedure注解在Oracle场景下基本不可用
Spring Data JPA的@Procedure只适合简单IN/OUT标量值(如int、string),它底层调用的是EntityManager.createNamedStoredProcedureQuery(),而Oracle游标需要手动执行+取结果,@Procedure无法处理getResultList()这类操作。
典型失败表现:方法返回void或基础类型,但实际执行后getOutputParameterValue("cur")为null,或抛IllegalArgumentException: Unknown parameter。
- 别在Repository接口里写
@Procedure(name = "xxx") List<user> callProc(...)</user>—— 这种写法对Oracle无效 - 想用注解方式,必须搭配
@NamedStoredProcedureQuery+resultClasses+@SqlResultSetMapping,但配置繁琐且不灵活 - 真实项目中,90%以上Oracle存储过程调用都直接走
EntityManager原生API
用EntityManager手动调用最稳
这是目前最可靠、最易调试的方式,绕过所有注解限制,完全控制参数注册和结果提取逻辑。
@PersistenceContext
private EntityManager em;
<p>public List<object> callOracleProc(String param) {
StoredProcedureQuery query = em.createStoredProcedureQuery("pkg_name.find_by_name");
query.registerStoredProcedureParameter("p_name", String.class, ParameterMode.IN);
query.registerStoredProcedureParameter("p_result", Void.class, ParameterMode.REF_CURSOR);
query.setParameter("p_name", param);
query.execute();
return query.getResultList(); // 返回List<object>,每行是Object数组
}</object></object></p>
- 返回值是
List<object></object>,不是实体列表,字段顺序严格对应SQLSELECT字段顺序 - 若要转成实体,得自己循环映射:
new User((String)row[0], (Long)row[1]) - 事务必须显式标注
@Transactional,否则execute()可能抛TransactionRequiredException - 别漏掉
query.execute()—— 没这句,getResultList()永远为空
Oracle OUT参数类型匹配很关键
Oracle的NUMBER(10,0)对应Java的Long,VARCHAR2对应String,DATE对应java.time.LocalDateTime或java.util.Date(取决于Hibernate版本)。类型不一致会导致ClassCastException或null值。
- IN参数:按数据库字段类型选Java类,比如Oracle
NUMBER→Long.class或BigDecimal.class(推荐后者防溢出) - OUT参数(非游标):
getOutputParameterValue("xxx")返回的是JDBC原生对象,NUMBER常返回BigDecimal,别直接强转int - 时间类型务必统一:Oracle
DATE建议用LocalDateTime.class注册,避免java.util.Date时区陷阱
Oracle存储过程调用真正的难点不在语法,而在类型绑定和游标生命周期管理——REF_CURSOR必须注册、必须执行、必须立刻取结果,中间任何一步断开,游标就失效。











