JPA createStoredProcedureQuery выдает «ORA-01000: превышено максимальное количество открытых курсоров» при использовании Hibernate - PullRequest
0 голосов
/ 13 мая 2018

Я пытаюсь вызвать хранимую процедуру Oracle, используя «createStoredProcedureQuery» EntityManager следующим образом:

@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>

Ответы [ 3 ]

0 голосов
/ 07 июня 2018

Вот мое решение:

int           retValue = -1;
ProcedureCall query    = null;

try {
  query = (ProcedureCall)entityManager.createNamedStoredProcedureQuery("MyStoredProcedure");

  query
    .setParameter("param1", value1)
    .setParameter("param2", value2)
    .execute();

  retValue = (int)query.getOutputParameterValue("outParam");
} finally {
  query.getOutputs().release();
}
0 голосов
/ 13 февраля 2019

Механизм обработки CallableStatement по умолчанию

При вызове метода execute в JPA StoredProcedureQuery или outputs().getCurrent() в Hibernate ProcedureCall Hibernate выполняет следующие действия:

enter image description here

Обратите внимание, что JDBC CallableStatement подготовлен и сохранен в связанном объекте ProcedureOutputsImpl. При вызове метода getOutputParameterValue Hibernate будет использовать базовый CallableStatement для извлечения параметра OUT.

По этой причине базовый JDBC CallableStatement остается открытым даже после выполнения хранимой процедуры и извлечения параметров OUT или REF_CURSOR.

Теперь, по умолчанию, CallableStatement закрывается после завершения текущей запущенной транзакции базы данных, либо посредством вызова commit или rollback.

enter image description here

Закрытие оператора 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 обеспечивает закрытие 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");
    }
});

Для получения более подробной информации, ознакомьтесь с этой статьей .

0 голосов
/ 14 мая 2018

В конце концов я нашел решение, я заменил строку

qr.execute();

на

qr.executeUpdate();

Согласно документации: «Когда executeUpdate вызывается для объекта StoredProcedureQuery, провайдер будет вызывать executeдля невыполненного запроса хранимой процедуры, за которым следует getUpdateCount. Результатами executeUpdate будут результаты getUpdateCount "

Но они не сказали ничего, что касается закрывающих курсоров, но мой метод теперь работает хорошо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...