Quante volte vi e’ capitato di eseguire una query su una tabella con diversi milioni di record e ad un certo punto ottenere il fantastico java.lang.OutOfMemoryError, a me svariate, ma si puo evitare…
Livello:
Facile
Per chi:
Chiunque abbia a che fare con database enormi ;)
Scenario:
Effettuare una select evitando un OutOfMemory
Il problema si presenta quando i driver per il database al quale ci si conette non supportano lo streaming del resultset, quindi lo statement carica in memoria tutti i record possibili sino a quando la povera JVM non ce la fa piu’ e decide, senza tanti complimenti, che per lei il lavoro termina li restituendo l’eccezione piu’ odiosa, OutOfMemory.
Questo problema non lo ho mai riscontrato ne su Oracle ne su MsSql mentre su PostgreSql e MySql si!
Ecco i due sistemi che ho utilizzato per “ingannare” i suddetti DB.
MySql
Statement stmt = conn.createStatement(); stmt.setFetchSize(Integer.MIN_VALUE); ResultSet rset = stmt.executeQuery("select * from tabellone"); while(rset.next()) { // faccio quello che devo... } // chiudo connessione,stmt e rset.
PostgreSql
Statement stmt = conn.createStatement(); stmt.setFetchSize(1); conn.setAutoCommit(false); ResultSet rset = stmt.executeQuery("select * from tabellone"); while(rset.next()) { // faccio quello che devo... } conn.commit(); // chiudo connessione,stmt e rset.
in particolare in una delle mie applicazioni, che puo collegarsi a diversi db server dove devo copiare dei dati da portare in locale, ho utlizzato questo sistema tramite una interfaccia cosi da poter gestire le eccezioni dei diversi db server
Lo scheletro e’ vagamente simile a questo:
L’interfaccia
import java.util.Properties; public interface RemoteDatabase { public void setRemoteConnection(Properties config); public void setLocalConnection(Properties config); public void copyData(); }
L’oggetto db generico dove impostare connessioni, statement etc etc
import java.util.Properties; public class CopyDataBase { public void setRemoteConnection(Properties config) { } public void setLocalConnection(Properties config) { } public void copyData() { // copia dei dati per sql generici } }
L’oggetto specifico per mysql
public class Mysql extends CopyDatabase { public void copyData() { // copia dei dati con lo "streaming" per mysql } }
L’oggetto specifico per postgresql
public class PostgreSql extends CopyDatabase { public void copyData() { // copia dei dati con lo "streaming" per postgres } }
Fatto questo non ci resta che scrivere la porzione di codice che ci interessa per fare la copia dei dati:
// logicamente questo lo ricaverete dinamicamente // a seconda del tipo di db remoto String remoteDBType = "Mysql"; RemoteDatabase rd = (RemoteDatabase) Class.forName(remoteDBType).newInstance(); // qui potranno esserci due oggetti Properties "source" e "destination" // contenenti le info necessarie per connettersi ai rispettivi // db server rd.setLocalConnection(new Properties()); rd.setRemoteConnection(new Properties()); // qui viene eseguita la copia dei dati rd.copyData();
Ho scritto una porzione di codice decisamente schelettrica :) ma spero proprio che sia comprensibile il senso di quello che ho voluto esporre, questa porzione di codice puo’ anche essere utile a chi si avvicina per la prima volta a questo linguaggio cosi da poter interpretare meglio l’uso delle interfacce.
Ciao e alla prossima,
Cristian.
RSS feed for comments on this post · TrackBack URI
Leave a reply
You must be logged in to post a comment.