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.
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! –
@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) –
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 –