Временное табличное пространство CLOB не освобождено

У меня проблема в том, что мое Java-приложение экспортирует большее количество clobs из базы данных, но всегда не хватает временного табличного пространства, поскольку старые clobs не освобождаются.

Упрощенный пример кода, как я это делаю:

public void getClobAndDoSomething (oracle.jdbc.OracleCallableStatement pLSQLCodeReturningClob) {
    try (OracleCallableStatement statement = pLSQLCodeReturningClob) {
        statment.registerOutParameter(1, Types.CLOB);
        statement.execute();

        oracle.sql.CLOB clob = statement.getCLOB(1);
        clob.open(CLOB.MODE_READONLY);
        Reader reader = clob.getCharacterStream();
        BufferedReader bufferedReader = new BufferedReader(reader);

        doSomethingWithClob(bufferedReader);

        bufferedReader.close();
        reader.close();
        clob.close();
        clob.freeTemporary();
    } catch (SQLException e) {
        if (e.getErrorCode() == 1652) {
            //Server ran out of temporary tablespace
        } else
            handleException(e);
    } catch (IOException e) {
         handleException(e);
    }
}

Если этот метод вызывается в цикле, в какой-то момент ему всегда будет не хватать временного табличного пространства.

Единственный надежный способ освободить место — закрыть соединение и открыть новое (например, с помощью clob.getInternalConnection.close()), но это замедлит работу приложения и сделает текущий многопоточный подход непригодным для использования.

К сожалению, документация оракула на ojdbc не очень полезна, и Google нашел только статьи, в которых мне предлагалось использовать метод free() лобов, который даже не реализован во временных клобах оракулов.

Дополнительное примечание.
Эта проблема также возникает при использовании оракулов APEXExport.class для экспорта большой рабочей области.

Особенности драйвера и системы:

  • ОС: Windows 7 Профессиональная x64
  • Java: 1.8.0_45 64-разрядная версия
  • ojdbc: 6 (Есть более конкретные версии?)
  • База данных: Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 — 64-битная производственная версия

Тестовый код, если у вас есть APEX-приложение:

java.sql.Connection con = getConnection();
String gStmtGetAppClob = "begin ? := wwv_flow_utilities.export_application_to_clob(?, ?, ?, ?); end;";
int appId = 100;

while (true) {
    OracleCallableStatement exportApplicationToClob = (OracleCallableStatement) con.prepareCall(gStmtGetAppClob);
    exportApplicationToClob.setString(3, "Y"); //Public reports
    exportApplicationToClob.setString(4, "N"); //Saved reports
    exportApplicationToClob.setString(5, "N"); //Interactive report notifications
    exportApplicationToClob.setBigDecimal(2, new BigDecimal(appId));

    getClobAndDoSomething(exportApplicationToClob);
    try {
        Thread.sleep(50);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        break;
    }
}
con.close();

Обновление:
После дополнительного тестирования я обнаружил, что в какой-то момент clobs освобождаются без закрытия соединения. Так что похоже, что free() на самом деле lazyFree(). Но это может занять больше минуты.
Я также могу преобразовать CLOB в Clob, не знаю, что я раньше делал не так. Проблема остается неизменной при использовании Clob.


person Simon M    schedule 14.08.2015    source источник
comment
У меня никогда не было таких проблем, когда я придерживался стандартного API JDBC, не используя API ojdbc. Убедитесь, что вызов free() также выполняется в случае исключения!   -  person Lukas Eder    schedule 14.08.2015
comment
@LukasEder Хорошая мысль с free() в блоке catch. В пример не вставил. К сожалению, я не могу использовать стандартный JDBC API, так как БД возвращает временный файл. Стандартный JDBC может обрабатывать только клобы, хранящиеся в таблице. (Приведение или преобразование приводит к исключению)   -  person Simon M    schedule 14.08.2015
comment
Извините, я пропустил эту деталь. Возможно, вы могли бы обновить свой вопрос, указав выдержку из PL/SQL, которую вы вызываете? Или, что еще лучше, минимальный воспроизводимый пример тоже был бы великолепен.   -  person Lukas Eder    schedule 14.08.2015


Ответы (1)


В мире pl/sql это было бы обработано с помощью временного CLOB и повторного использования его внутри цикла.

Предполагая, что вы используете java.sql.CLOB., похоже, у него нет опции createTemporary CLOB, но есть oracle.sql.CLOB. Он также имеет метод freeTemporary() для очистки временного пространства.

https://docs.oracle.com/cd/E18283_01/appdev.112/e13995/oracle/sql/CLOB.html

Ваша вызывающая процедура может создать временный CLOB и передать его в качестве параметра (скажем, p_clob) этому методу. Каждый раз назначайте возвращаемое значение запроса p_clob вместо создания нового CLOB (например, CLOB clob = statement.getCLOB).

Сейчас мало времени, но позже отредактирую подробный код. Если вы можете работать с выше, то хорошо.

person Brainhash    schedule 14.08.2015
comment
CLOB, который я использую, уже является oracle.sql.CLOBjava.sql.Clob есть только заглавная C), но я добавлю точные классы в пример для завершения. Повторное использование одного и того же CLOB - хорошая идея для решения проблемы, но только вариант, если я вызываю пользовательские PL/SQL-пакеты, а не для пакетов СУБД. - person Simon M; 14.08.2015
comment
как насчет создания Clob перед циклом (While Loop) ... и очистки его и повторного использования того же Clob внутри цикла. - person Brainhash; 15.08.2015
comment
Я не уверен, как я мог бы повторно использовать CLOB в этой ситуации, по крайней мере, когда я вызываю стандартные пакеты (например, wwv_flow_utils). Они всегда будут возвращать мне новый CLOB, поэтому сохранение старой ссылки не даст мне никаких преимуществ. - person Simon M; 17.08.2015