2015-08-14 8 views
5

Ho il problema che la mia applicazione Java sta esportando una quantità maggiore di clob da un database, ma sempre esaurisce il tablespace temporaneo poiché i vecchi clob non vengono liberati.Spazio tabelle temporaneo di CLOB non liberato

Un codice di esempio semplificato come lo faccio sarebbe:

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

Se questo metodo viene chiamato in un ciclo sarà sempre finiscono per esaurire lo spazio tabella temporanea ad un certo punto.

L'unico modo affidabile per liberare lo spazio è chiudendo la connessione e aprendone una nuova (ad esempio utilizzando clob.getInternalConnection.close()) ma ciò rallenterebbe l'applicazione e renderebbe inutilizzabile l'approccio multi-thread corrente.

Purtroppo la documentazione di Oracle su ojdbc non è molto utile e Google ha trovato solo articoli che mi dicevano di usare il metodo free() di lobs che non è nemmeno implementato dai clob temporanei di Oracle.

nota complementare:
Questo problema si verifica anche quando si utilizza oracoli APEXExport.class per esportare un grande spazio di lavoro.

driver e di sistema specifiche:

  • SO: Windows 7 Professional x64
  • Java: 1.8.0_45 64-bit
  • ojdbc: 6 (? Ci sono versioni più specifiche)
  • database : Oracle Database 11g Enterprise Edition versione 11.2.0.1.0 - Produzione a 64 bit

Codice di prova se si dispone di un'applicazione 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(); 

Aggiornamento:
Dopo ulteriori test ho scoperto che i CLOB sono sempre liberati ad un certo punto, senza chiudere la connessione. Quindi sembra che lo free() sia in realtà un lazyFree(). Ma questo può richiedere più di un minuto.
Posso anche convertire CLOB in Clob, non so cosa stavo facendo male in precedenza. Il problema rimane invariato se si utilizza Clob.

+1

Non ho mai avuto nessuno di questi problemi quando si attaccano all'API standard JDBC, non utilizzando l'API ojdbc. Assicurati che anche la chiamata 'free()' sia eseguita nel caso di eccezione! –

+0

@LukasEder Good Point con 'free()' nel blocco catch. Non l'ho messo nell'esempio. Sfortunatamente non posso usare l'API JDBC standard poiché il DB restituisce un clob temporaneo. JDBC standard può gestire solo clob memorizzati in una tabella. (Trasmettere o convertire conduce a un'eccezione) –

+0

Mi dispiace, ho perso quel dettaglio. Forse potresti aggiornare la tua domanda con un estratto del PL/SQL che stai chiamando? O ancora meglio, un esempio riproducibile minimo sarebbe grande, anche –

risposta

0

Nel mondo pl/sql questo sarebbe stato gestito tramite CLOB temporaneo e riutilizzandolo all'interno del ciclo.

Supponendo che si stia utilizzando java.sql.CLOB, non sembra aver creato l'opzione CLOB temporanea, ma oracle.sql.CLOB lo fa. Ha anche il metodo freeTemporary() per cancellare lo spazio temporaneo.

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

tua routine chiamante può creare un CLOB temporanea e passarlo come parametro (diciamo p_clob) a questo metodo. Assegna il valore di ritorno della query a p_clob ogni volta invece di creare un nuovo CLOB (ad esempio, CLOB clob = statement.getCLOB).

In breve tempo, ma in seguito verrà modificato un codice dettagliato. Se puoi lavorare con sopra, allora bene.

+0

Il CLOB che sto usando è già un 'oracle.sql.CLOB' (il' java.sql.Clob' ha solo la C maiuscola), ma aggiungo le esatte classi dell'esempio per il completamento. Riutilizzare lo stesso CLOB è una buona idea per aggirare il problema, ma solo un'opzione se chiamo pacchetti PL/SQL personalizzati, non per pacchetti DBMS. –

+0

come creare Clob prima del ciclo (While Loop) ... e pulirlo e riutilizzare lo stesso Clob all'interno del ciclo. – Brainhash

+0

Non sono sicuro di come riutilizzare un CLOB in questa situazione, almeno quando chiamo pacchetti standard (come wwv_flow_utils). Mi restituiranno sempre un nuovo CLOB, quindi mantenere il vecchio riferimento non mi darebbe alcun vantaggio. –

Problemi correlati