java - JPA createStoredProcedureQuery 在使用 Hibernate 时抛出 "ORA-01000: maximum open cursors exceeded"

标签 java oracle hibernate jpa stored-procedures

我尝试通过这种方式使用 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 上的方法 StoredProcedureQueryoutputs().getCurrent()在 Hibernate 上 ProcedureCall , Hibernate 执行以下操作:

Stored procedure sequence diagram

注意 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 .

Entity transaction

尽快关闭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/

相关文章:

sql - 不能删除系统生成的序列

sql - Oracle SQL - 从给定列表中选择不同的值

java - 使用 Hibernate 实体的域实体?

java - Android Visualizer 立体捕捉

java - 如何在 RestTemplate 上应用 PowerMockito

java - Struts2:在上下文路径中创建一个新文件并写入

mysql - 是否可以使用与数据库无关的 SQL 查询来获取前 N 行?

hibernate - JPA 返回我的实体,仅填充了 id,所有其他字段均为空

java - Play Framework 1.2.5 Hibernate 升级到 v 4.2.4

java - Eclipse 始终构建工作区