I set di risultati e le dichiarazioni JDBC devono essere chiusi separatamente anche se in seguito la connessione viene chiusa?


256

Si dice che sia una buona abitudine chiudere tutte le risorse JDBC dopo l'uso. Ma se ho il seguente codice, è necessario chiudere il gruppo di risultati e la dichiarazione?

Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
    conn = // Retrieve connection
    stmt = conn.prepareStatement(// Some SQL);
    rs = stmt.executeQuery();
} catch(Exception e) {
    // Error Handling
} finally {
    try { if (rs != null) rs.close(); } catch (Exception e) {};
    try { if (stmt != null) stmt.close(); } catch (Exception e) {};
    try { if (conn != null) conn.close(); } catch (Exception e) {};
}

La domanda è se la chiusura della connessione fa il lavoro o se lascia alcune risorse in uso.


Possibile duplicato delle connessioni al database
Austin Schäfer,

Risposte:


199

Quello che hai fatto è una pratica perfetta e molto buona.

Il motivo per cui dico la sua buona pratica ... Ad esempio, se per qualche motivo stai usando un tipo "primitivo" di pooling di database e chiami connection.close(), la connessione verrà restituita al pool e il ResultSet/ Statementnon verrà mai chiuso e quindi tu incontrerà molti nuovi problemi diversi!

Quindi non puoi sempre contare su connection.close()per ripulire.

Spero che aiuti :)


4
... e il motivo più evidente per chiudere tutto in modo esplicito.
Zeemee,

2
Concordo sul fatto che è buona pratica chiudere serie di risultati e dichiarazioni. Tuttavia, i set di risultati e le dichiarazioni vengono raccolti in modo inutile: non rimangono aperti per sempre e non si "incontrano molti nuovi problemi diversi".
stepanian,

3
@Ralph Stevens - Non puoi contare su questo. Ho avuto una situazione in cui il driver JSBC MSSQL ha perso la memoria perché i ResultSet non sono stati chiusi, anche dopo essere stati raccolti.
Paul,

7
@Paul - Interessante. Mi sembra un difetto del driver JDBC.
stepanian,

2
@tleb: funzionerebbe come previsto. anche se in teoria, le eccezioni sono "costose", quindi ci sarebbe un piccolissimo colpo di scena (che hai già identificato)
Paul,

124

Java 1.7 ci semplifica la vita grazie alla dichiarazione try-with-resources .

try (Connection connection = dataSource.getConnection();
    Statement statement = connection.createStatement()) {
    try (ResultSet resultSet = statement.executeQuery("some query")) {
        // Do stuff with the result set.
    }
    try (ResultSet resultSet = statement.executeQuery("some query")) {
        // Do more stuff with the second result set.
    }
}

Questa sintassi è piuttosto breve ed elegante. E connectionsarà davvero chiuso anche quando statementnon è stato possibile crearlo.


56
Non è necessario nidificare in questo modo, puoi fare tutto in un solo tentativo con risorse, basta trattare le dichiarazioni delle risorse come dichiarazioni separate (separate da ;)
Mark Rotteveel,

2
Mark Rotteveel: è possibile utilizzare un singolo tentativo per tutte e tre le connessioni, istruzioni e set di risultati, ma se si desidera eseguire diverse query, è necessario chiudere il set di risultati precedente prima di iniziare una nuova query. Almeno questo è il modo in cui il DBMS che stavo usando ha funzionato.
Raúl Salinas-Monteagudo,

perché non fai qualcosa del genere? try (connessione aperta) {try (più istruzioni e set di risultati) {specialmente quando i risultati delle query successive possono essere calcolati con quelli precedenti.
Daniel Hajduk,

Daniel: Quando ho usato quel modello, il backend JDBC sottostante non supportava il mantenimento di un ResultSet e l'apertura di un secondo.
Raúl Salinas-Monteagudo,

rascio, puoi fare tutto il necessario nel blocco di cattura
Raúl Salinas-Monteagudo

73

Dai javadocs :

Quando un Statementoggetto viene chiuso, anche il suo ResultSetoggetto corrente , se esistente, viene chiuso.

Tuttavia, i javadocs non sono molto chiari sul fatto che Statemente ResultSetsiano chiusi quando si chiude il sottostante Connection. Dichiarano semplicemente che la chiusura di una connessione:

Rilascia Connectionimmediatamente il database di questo oggetto e le risorse JDBC invece di attendere il loro rilascio automatico.

A mio avviso, sempre esplicitamente vicino ResultSets, Statementse Connectionsquando hai finito con loro come l'implementazione di closepuò variare tra i driver di database.

Puoi risparmiare un sacco di codice sulla piastra della caldaia usando metodi come closeQuietlyin DBUtils di Apache.


1
Grazie dogbane. Il punto è che non puoi dipendere dall'implementazione di Connection.close, giusto?
Zeemee,


39

Ora sto usando Oracle con Java. Ecco il mio punto di vista:

Si dovrebbe chiudere ResultSete Statementin modo esplicito perché Oracle ha problemi in precedenza con mantenere i cursori aperti anche dopo la chiusura della connessione. Se non si chiude il ResultSet(cursore), verrà generato un errore come il numero massimo di cursori aperti superato .

Penso che potresti riscontrare lo stesso problema con altri database che usi.

Ecco il tutorial Chiudi ResultSet al termine :

Chiudi Risultato Impostato al termine

Chiudi ResultSetoggetto non appena si finisce di lavorare con l' ResultSetoggetto anche se l' Statementoggetto chiude ResultSetimplicitamente l' oggetto quando si chiude, la chiusura ResultSetesplicita dà la possibilità al garbage collector di recuperare la memoria il prima possibile perché l' ResultSetoggetto può occupare molta memoria a seconda della query.

ResultSet.close();


Grazie Hilal, questi sono buoni motivi per chiuderlo il prima possibile. Tuttavia, importa se ResultSet e Statement sono chiusi direttamente prima della connessione (ciò significa in alcuni casi: non il prima possibile)?
Zeemee,

Se si chiude la connessione, si chiuderà tutto la dichiarazione del gruppo di risultati ans anche, ma si dovrebbe chiudere di risultati prima che la connessione

E perché dovrei chiudere il gruppo di risultati prima della connessione? Intendi a causa dei problemi del driver Oracle?
Zeemee,

1
qui è più generale chiarimento :) stackoverflow.com/questions/103938/...

In teoria, se si chiude la dichiarazione non deve chiudere i gruppi di risultati, ma probabilmente è buona pratica.
rogerdpack,

8

Se vuoi un codice più compatto, ti suggerisco di usare Apache Commons DbUtils . In questo caso:

Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
    conn = // Retrieve connection
    stmt = conn.prepareStatement(// Some SQL);
    rs = stmt.executeQuery();
} catch(Exception e) {
    // Error Handling
} finally {
    DbUtils.closeQuietly(rs);
    DbUtils.closeQuietly(stmt);
    DbUtils.closeQuietly(conn);
}

3
cosa accadrà se uso questo codice invece di rs.close (), stmt.close (), conn.close ()
Onkar Musale

3

Il metodo corretto e sicuro per chiudere le risorse associate a JDBC questo (tratto da Come chiudere correttamente le risorse JDBC - Ogni volta ):

Connection connection = dataSource.getConnection();
try {
    Statement statement = connection.createStatement();

    try {
        ResultSet resultSet = statement.executeQuery("some query");

        try {
            // Do stuff with the result set.
        } finally {
            resultSet.close();
        }
    } finally {
        statement.close();
    }
} finally {
    connection.close();
}

3

Non importa se Connectionè raggruppabile o meno. Anche la connessione poolable deve essere pulita prima di tornare al pool.

"Pulito" di solito significa chiudere i set di risultati e ripristinare eventuali transazioni in sospeso ma non chiudere la connessione. Altrimenti il ​​pool perde il suo senso.


2

No, non è necessario chiudere nulla MA la connessione. Secondo le specifiche JDBC, la chiusura di qualsiasi oggetto superiore chiuderà automaticamente gli oggetti inferiori. La chiusura Connectionchiuderà qualsiasi Statements creato dalla connessione. Chiudendo qualsiasi Statementsi chiuderanno tutti gli ResultSets che sono stati creati da quello Statement. Non importa se Connectionè raggruppabile o meno. Anche la connessione poolable deve essere pulita prima di tornare al pool.

Ovviamente potresti avere lunghi cicli nidificati sulla Connectioncreazione di molte istruzioni, quindi chiuderle è appropriato. Non mi chiudo quasi mai ResultSet, sembra eccessivo quando si chiude Statemento Connectionli chiuderò .


1

Ho creato il seguente metodo per creare One Liner riutilizzabile:

public void oneMethodToCloseThemAll(ResultSet resultSet, Statement statement, Connection connection) {
    if (resultSet != null) {
        try {
            if (!resultSet.isClosed()) {
                resultSet.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (statement != null) {
        try {
            if (!statement.isClosed()) {
                statement.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    if (connection != null) {
        try {
            if (!connection.isClosed()) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Uso questo codice in una classe padre ereditata da tutte le mie classi che inviano query DB. Posso usare l'Oneliner su tutte le query, anche se non ho un resultSet. Il metodo si occupa di chiudere ResultSet, Statement, Connection nell'ordine corretto. Ecco come appare il mio blocco finalmente.

finally {
    oneMethodToCloseThemAll(resultSet, preStatement, sqlConnection);
}


-1

Alcune funzioni utili:

public static void silentCloseResultSets(Statement st) {
    try {
        while (!(!st.getMoreResults() && (st.getUpdateCount() == -1))) {}
    } catch (SQLException ignore) {}
}
public static void silentCloseResultSets(Statement ...statements) {
    for (Statement st: statements) silentCloseResultSets(st);
}

Non c'è nulla qui che chiude qualcosa. Solo un ciclo inutile che legge inutilmente l'intera risposta, anche se chiaramente non è più desiderato.
Marchese di Lorne,

-1

Con il modulo Java 6 penso sia meglio verificare che sia chiuso o meno prima di chiudere (ad esempio se un pool di connessioni sfrutta la connessione in un altro thread) - ad esempio qualche problema di rete - l'istruzione e lo stato del gruppo di risultati possono essere chiusi. (non succede spesso, ma ho avuto questo problema con Oracle e DBCP). Il mio modello è che (nella vecchia sintassi Java) è:

try {
    //...   
    return resp;
} finally {
    if (rs != null && !rs.isClosed()) {
        try {
            rs.close();
        } catch (Exception e2) { 
            log.warn("Cannot close resultset: " + e2.getMessage());
        }
    }
    if (stmt != null && !stmt.isClosed()) {
        try {
            stmt.close();
        } catch (Exception e2) {
            log.warn("Cannot close statement " + e2.getMessage()); 
        }
    }
    if (con != null && !conn.isClosed()) {
        try {
            con.close();
        } catch (Exception e2) {
            log.warn("Cannot close connection: " + e2.getMessage());
        }
    }
}

In teoria non è perfetto al 100% perché tra il controllo dello stato vicino e la stessa chiusura c'è un piccolo spazio per il cambiamento di stato. Nel peggiore dei casi riceverai un avviso a lungo. - ma è inferiore alla possibilità di cambio di stato nelle query a lungo termine. Stiamo usando questo modello in produzione con un carico "medio" (150 utenti simultanei) e non abbiamo avuto problemi con esso - quindi non vedere mai quel messaggio di avviso.


Non hai bisogno dei isClosed()test, perché chiudere uno di questi che è già chiuso è una no-op. Ciò elimina il problema della finestra di temporizzazione. Che sarebbe anche eliminato eliminando le variabili locali Connection, Statemente ResultSet.
Marchese di Lorne,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.