Newsgroup: it.comp.java
Titolo: Chiusura Statement e ResultSet

La domanda è molto semplice e si può riassumere così: Statement e ResultSet 
vanno chiusi entrambi dopo l'uso, oppure mi posso permettere di chiudere 
solo il ResultSet?
Entrando nel dettaglio, spiego da dove nasce il mio dubbio. In una classe 
ho implementato un metodo che restituisce al chiamante un ResultSet, il 
PreparedStatement che lo ha generato rimane privato di questo metodo. 
Chiaramente il metodo non può chiuderlo perchè altrimenti restituirebbe un 
ResultSet non valido. Della chiusura del ResultSet se ne occupa il 
chiamante, quindi di norma lo Statement non viene mai esplicitamente 
chiuso.
Il problema è emerso quando dal metodo si è scatenata un eccezione per un 
errore durante l'esecuzione della query. In quel caso, casualmente il 
programma è entrato in un loop (per un banale errore di flusso) e in breve 
l'eccezione è diventata "Troppi cursori aperti" dovuto evidentemente alla 
non-chiusura dello Statement.
E' corretto secondo voi che il metodo chiuda esplicitamente lo Statement 
*solo* in caso di errore? Oppure faccio bene a preparare una test-suite che 
"stressa" la procedura in modo da vedere cosa succederebbe looppando a 
oltranza senza errore?



Credo che le guideline di sun riguardo jdbc dicano di chiudere sia il
PreparedStatement che il ResultSet.
Io di solito passo i risultati del ResultSet su array e restituisco
quello, qualcosa del tipo:
public ArrayList metodoSQL() {
    ArrayList result = new ArrayList();
    String selectStatement =
    "SELECT ... "+
    "FROM ... bla, bla, ORDER BY foo";
    PreparedStatement prepStmt = null;
    ResultSet rs = null;
    try {
/*
la connessione con viene precedentemente inizializzata a livello di
costruttore (in realtà è un metodo di un EJB)
*/
      prepStmt = con.prepareStatement(selectStatement);
      prepStmt.setString(1, fk_cod_azienda);
      rs = prepStmt.executeQuery();
      while (rs.next()) {
        String[] rigaOut = new String[2];
        rigaOut[0] = rs.getString("foo");
        rigaOut[1] = rs.getString("foobar");
        result.add(rigaOut);
      }
      rs.close(); rs = null;
      prepStmt.close(); prepStmt = null;
    } catch (SQLException ex)  {
	/*gestisci come ti pare la SQLException*/
    } finally {
      if (rs != null) {
        try { rs.close(); } catch (SQLException e) {}
        rs = null;
      }
      if (prepStmt != null) {
        try { prepStmt.close(); } catch (SQLException e) {}
        prepStmt = null;
      }
    }
    return result;
  }

 ...
Ciao Vincent.
Dunque, da buon programmatore proveniente dal mondo del C/C++, sono
abituato a chiudere e deallocare anche mia nonna quando questa non serve
più. :)
Quando avevo cominciato con java, mi avevano detto che non era necessario
chiudere gli oggetti (cosa che mi pareva strana).
Va bene mi sono detto, sei tu il guru java ed avrai ragione. Quando
l'applicativo (web) è stato portato in produzione avevamo sempre l'errore
di numero cursori eccedenti. Alla fine era dovuto al connection pooling
che mantenendo aperte le connessioni, manteneva attivi i cursori.
Molto probabilmente se non usi un connection pooling effettivamente quando
chiudi la connessione, chiudi a cascata statement e resultset.
Insomma io sono tornato alle mie vecchie abitudini e nel finally metto
sempre un bel rs.close() stmt.close().
Ciao D.
Inviato da 
www.mynewsgate.net


Di norma conviene disaccoppiare completamente l'API JDBC: cio' significa 
creare interfacce applicative idonee al tuo contesto, ma che non 
facciano "filtrare" in modo esplicito quelle dell'API JDBC.
In altre parole, NON conviene QUASI MAI passare
direttamente dei ResultSet, a meno che tu non abbia la assoluta
certezza di poterne controllare il "ciclo di vita".
Ad esempio,
ad una classe del tipo
public class UnaClasse
{
      public ResultSet faiQualcosa(...)
}
che venga utilizzata direttamente in molte sezioni della tua
applicazione, e' sicuramente preferibile il seguente approccio
(anche se piu' elaborato):
/**
  * Questa e' l'interfaccia applicativa da utilizzare
  * al posto dei ResultSet.
  * Potrebbe essere un generico java.util.Iterator, oppure
  * (meglio!) una interfaccia creata ad-hoc.
  * La sua struttura dipende ovviamente dal contesto applicativo...
  */
interface Contenitore
{
	public <Oggetto/Interfaccia Applicativo> faiQuesto();
         public <Oggetto/Interfaccia Applicativo> faiAltro();
}
/**
  * Questa e' l'interfaccia utilizzata per disaccoppiare
  * l'accesso al supporto di persistenza dal resto
  * dell'applicazione.
  * Anche qui, numero e semantica dei metodi dipendono
  * dal contesto applicativo
  */
interface PersistenceFrontEnd
{
      public Contenitore faiQualcosa(...) throws ...;
      public Contenitore faiAltro(...) throws ...;
      ...
}
/**
  * Questa classe puo' essere utilizzata come base per le
  * implementazioni concrete del PersistenceFrontEnd
  * che accedono ad un database relazionale tramite JDBC
  */
public abstract class PersistenceFrontEndPerJDBC
{
      // La connection potrebbe essere un "Decorator" che
      // "maschera" il connection pooling
      private Connection conn;	
      /**
       */
      public Contenitore faiQualcosa(...)
      {
          Contenitore ctn = null;
          try
          {
	   <imposta la connessione>
            Statement stmt = <crea e/o imposta lo Statement JDBC>;
            ResultSet rst = <usa lo Statement per accedere al ResultSet>;
            ctn = riempiIlContentiore(rst);
          }
          catch (...)
          {
            <gestione delle eccezioni>
          }
          finally
          {
            <chiudi ResultSet>
            <chiudi Statement>
            <chiudi/resetta la connessione>
          }
          return ctn;
      }
      /**
       */
      public Contenitore faiAltro(...)
      {
         <implementazione analoga a faiQualcosa(...)>
      }
      /**
       * "Factory Method"
       * Alle implementazioni concrete e' delegata la responsabilita'
       * di determinare il tipo concreto del Contenitore e di
       * riempirlo con oggetti creati a partire dai dati "grezzi"
       * contenuti nel ResultSet.
       * In questo schema, le implementazioni concrete NON possono
       * tenersi un reference al ResultSet, ma lo devono "consumare"
       * immediatamente (per quello che gli compete), perche' una
       * volta ritornato il controllo al chiamante, il ResultSet
       * viene chiuso
       */
      protected abstract Contenitore riempiIlContenitore(ResultSet rst);
}
Realizzando un design basato su interfacce che espongono solo
entita' di alto livello, l'utilizzo di JDBC e' sempre indiretto
e tu sei libero di scegliere l'implementazione piu' idonea per
i tuoi scopi, tenendo sotto controllo la creazione e/o la
chiusura di ResultSet e Statement, implementando cache, pooling,
ecc. ecc; il tutto senza influire sul resto della tua applicazione.
Lo schema che ho abbozzato e' ovviamente uno dei piu' semplici
(e quindi anche uno dei meno versatili!).
Altri (molti!) approcci (li vogliamo chiamare Pattern) sono
possibili, ma in generale non esiste LA soluzione valida per
tutti i contesti. Occorre quindi effettuare una scelta oculata
che dipende dal tipo di applicazione e dalla esperienza di
chi la deve progettare.
Come al solito, non esistono risposte scolpite
nella pietra (per fortuna, altrimenti di cosa camperemmo!? ;)
Spero di esserti stato d'aiuto
Saluti
Roberto
-- 
+----------------------------------+
| Roberto Bagnoli                  |
| Sistemi Informativi e Telematici |
| Comune di Modena                 |
| 
robe...@comune.modena.it
        |
+----------------------------------+

 Spero di esserti stato d'aiuto
Decisamente si.
Grazie!

 Va bene mi sono detto, sei tu il guru java ed avrai ragione. 
Evidentemente non si trattava di un "guru"... ;)
Lavorando con Java fin dalla prima versione e
seguendo le linee guida trovate sulla documentazione (ufficale e non),
gia' da allora imparai a fare buon uso delle risorse critiche,
evitando di affidarmi ciecamente ed indiscriminatamante
al solo garbage collector.
Altrimenti, ad esempio, cosa ci starebbero a fare i metodi
close() nelle classi stream dei package 
java.io
 e nella classe
Connection di java.sql?
