2015-08-14 52 views
5

Mam problem, że moja aplikacja Java eksportuje większą liczbę brył z bazy danych, ale zawsze kończy się tymczasowy obszar tabel, ponieważ stare cloby nie są zwalniane.Tymczasowe tabele CLOB nie zostały zwolnione

Uproszczony przykład kod jak zrobić byłoby:

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); 
    } 
} 

Jeśli ta metoda jest wywoływana w pętli będzie zawsze kończy się na wyczerpaniu tymczasowego obszaru tabel w pewnym momencie.

Jedynym niezawodnym sposobem na zwolnienie miejsca jest zamknięcie połączenia i otwarcie nowego (na przykład za pomocą clob.getInternalConnection.close()), ale spowolniłoby to aplikację i uniemożliwiłoby korzystanie z wielowątkowego podejścia.

Niestety dokumentacja Oracle na ojdbc nie jest zbyt pomocna, a google tylko znajdowało artykuły mówiące o tym, żebym używał metody lobs, która nie jest nawet implementowana przez tymczasowe cloby oracles.

Uwaga dodatkowa:
Kwestia ta ma również wystąpić podczas korzystania wyrocznie APEXExport.class wyeksportować duży obszar roboczy.

kierowcy i systemowe specyfika:

  • System operacyjny: Windows 7 x64 Profesjonalne
  • Java: 1.8.0_45 64-Bit
  • ojdbc: 6 (? Czy istnieją bardziej szczegółowe wersje)
  • Database : Oracle Database 11g Enterprise Edition Wydanie 11.2.0.1.0 - Produkcja 64-bitowa

Kod testu, jeśli posiadasz aplikację 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(); 

Aktualizacja:
Po dalszych badaniach okazało się, że to CLOB są coraz uwolniony w pewnym momencie bez zamykania połączenia. Wygląda więc na to, że free() jest faktycznie lazyFree(). Ale może to zająć więcej niż minutę.
Mogę również przekonwertować CLOB na Clob, nie wiem, co robiłem źle wcześniej. Problem pozostaje niezmieniony, jeśli używasz Clob.

+1

Nigdy nie miałem żadnego z tych problemów, gdy trzymałem się standardowego API JDBC, nie używając interfejsu ojdbc API. Upewnij się, że wywołanie 'free()' jest również wykonywane w przypadku wyjątku! –

+0

@LukasEder Good Point z 'free()' w bloku catch. Nie wstawiłem tego do przykładu. Niestety nie mogę używać standardowego API JDBC, ponieważ DB zwraca tymczasowy clob. Standardowy JDBC może obsługiwać tylko cloby przechowywane w tabeli. (Przesyłanie lub przekształcanie prowadzi do wyjątku) –

+0

Przykro mi, tęskniłem za tym szczegółem. Być może mógłbyś zaktualizować swoje pytanie ekstraktem z PL/SQL, do którego dzwonisz? Lub jeszcze lepiej, minimalnie powtarzalny przykład byłby świetny, zbyt. –

Odpowiedz

0

W świecie pl/sql zostałoby to obsłużone przez tymczasowy CLOB i ponowne użycie go wewnątrz pętli.

Zakładając, że używasz java.sql.CLOB., Wydaje się, że nie ma on opcji createTemporary CLOB, ale oracle.sql.CLOB ma. Ma również metodę czyszczenia przestrzeni tymczasowej w trybie freeTemporary().

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

rutynowych wywołujący może utworzyć tymczasowy CLOB i przekazać ją jako parametr (powiedzmy p_clob) do tej metody. Przypisuj zwracaną wartość zapytania do p_clob za każdym razem, zamiast tworzyć nowy obiekt CLOB (np. CLOB clob = statement.getCLOB).

W tej chwili brakuje czasu, ale później zmienimy szczegółowy kod. Jeśli możesz pracować z powyższym, to dobrze.

+0

Używany CLOB jest już 'oracle.sql.CLOB' (' java.sql.Clob' ma tylko duże C), ale dodam dokładne klasy do przykładu na zakończenie. Ponowne użycie tego samego elementu CLOB jest dobrym pomysłem na obejście problemu, ale jest to tylko opcja, jeśli wywołuję niestandardowe pakiety PL/SQL, a nie pakiety DBMS. –

+0

, jak o tworzeniu Clob przed pętli (podczas pętli) .. i czyszczenia i ponownego użycia tego samego Clob wewnątrz pętli. – Brainhash

+0

Nie jestem pewien, w jaki sposób mogę ponownie użyć CLOB w tej sytuacji, przynajmniej wtedy, gdy nazywam standardowe pakiety (jak wwv_flow_utils). Zawsze zwrócą mi nowe CLOB, więc zachowanie starego odniesienia nie dałoby mi żadnej korzyści. –