我尝试通过这种方式使用 EntityManager 的“createStoredProcedureQuery”调用 Oracle 存储过程:
@Transactional(readOnly = false, propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
public void saveMeterVol(Meter meter, Double vol1, Chng chng, User user, Date dt1, Date dt2) {
StoredProcedureQuery qr = em.createStoredProcedureQuery("mt.P_METER.meter_vol_ins_upd_java");
qr.registerStoredProcedureParameter(1, Integer.class, ParameterMode.OUT);
qr.registerStoredProcedureParameter(2, Integer.class, ParameterMode.IN);
qr.registerStoredProcedureParameter(3, Integer.class, ParameterMode.IN);
qr.registerStoredProcedureParameter(4, Double.class, ParameterMode.IN);
qr.registerStoredProcedureParameter(5, Date.class, ParameterMode.IN);
qr.registerStoredProcedureParameter(6, Date.class, ParameterMode.IN);
qr.registerStoredProcedureParameter(7, String.class, ParameterMode.IN);
qr.setParameter(2, meter.getId());
qr.setParameter(3, chng.getId());
qr.setParameter(4, vol1);
qr.setParameter(5, dt1);
qr.setParameter(6, dt2);
qr.setParameter(7, user.getCd());
qr.execute();
}
当我调用此方法超过 300 次时,Oracle 出现异常: ORA-01000: 超出最大打开游标数
据我了解,Java 在调用我的过程后不会关闭 Oracle 游标,但我不明白为什么?
我试过
em.close();
但这并没有帮助。
我使用:
<spring-framework.version>5.0.5.RELEASE</spring-framework.version>
<hibernate.version>5.1.0.Final</hibernate.version>
<java.version>1.8</java.version>
最佳答案
默认CallableStatement
处理机制
调用 execute
时JPA 上的方法 StoredProcedureQuery
或 outputs().getCurrent()
在 Hibernate 上 ProcedureCall
, Hibernate 执行以下操作:
注意 JDBC CallableStatement
准备并存储在相关的 ProcedureOutputsImpl
中目的。调用 getOutputParameterValue
时方法,Hibernate 将使用底层 CallableStatement
获取 OUT
参数。
For this reason, the underlying JDBC CallableStatement remains open even after executing the stored procedure and fetching the OUT or REF_CURSOR parameters.
现在,默认情况下,CallableStatement
通过调用 commit
结束当前正在运行的数据库事务后关闭。或 rollback
.
尽快关闭JDBC语句
因此,关闭JDBC CallableStatement
您应该尽快调用 release
从存储过程中获取您想要的所有数据后:
StoredProcedureQuery query = entityManager
.createStoredProcedureQuery("count_comments")
.registerStoredProcedureParameter(
"postId",
Long.class,
ParameterMode.IN
)
.registerStoredProcedureParameter(
"commentCount",
Long.class,
ParameterMode.OUT
)
.setParameter("postId", 1L);
try {
query.execute();
Long commentCount = (Long) query
.getOutputParameterValue("commentCount");
assertEquals(Long.valueOf(2), commentCount);
} finally {
query.unwrap(ProcedureOutputs.class).release();
}
调用 release
相关的方法 ProcedureOutputs
finally
中的对象 block 确保 JDBC CallableStatement
无论存储过程调用的结果如何,都将关闭。
从 Hibernate 6 开始
现在,调用 release
手动有点乏味,所以我决定创建 HHH-13215 Jira 问题,从 Hibernate ORM 6 开始,您可以像这样重写前面的示例:
Long commentCount = doInJPA(entityManager -> {
try(ProcedureCall query = entityManager
.createStoredProcedureQuery("count_comments")
.unwrap(ProcedureCall.class)) {
return (Long) query
.registerStoredProcedureParameter(
"postId",
Long.class,
ParameterMode.IN
)
.registerStoredProcedureParameter(
"commentCount",
Long.class,
ParameterMode.OUT
)
.setParameter("postId", 1L)
.getOutputParameterValue("commentCount");
}
});
关于java - JPA createStoredProcedureQuery 在使用 Hibernate 时抛出 "ORA-01000: maximum open cursors exceeded",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50316970/