Fermeture des connexions à la base de données en Java

121

Je suis un peu confus, je lisais ce qui suit sur http://en.wikipedia.org/wiki/Java_Database_Connectivity

Connection conn = DriverManager.getConnection(
     "jdbc:somejdbcvendor:other data needed by some jdbc vendor",
     "myLogin",
     "myPassword" );

Statement stmt = conn.createStatement();
try {
    stmt.executeUpdate( "INSERT INTO MyTable( name ) VALUES ( 'my name' ) " );
} finally {
    //It's important to close the statement when you are done with it
    stmt.close();
}

Vous n'avez pas besoin de fermer la connexion conn? Que se passe-t-il vraiment si conn.close () ne se produit pas?

J'ai une application Web privée que je maintiens qui ne ferme actuellement aucun des formulaires, mais est-ce que l'important est vraiment celui de stmt, celui de conn ou les deux?

Le site continue de tomber par intermittence, mais le serveur continue de dire que c'est un problème de connexion à la base de données, je soupçonne qu'il n'est pas fermé, mais je ne sais pas lequel fermer le cas échéant.

onaclov2000
la source
Il est toujours recommandé de fermer les connexions vous-même, sans dépendre d'autres pilotes et modèles pour gérer la fermeture. L'échec de la fermeture de la connexion entraînera l'ouverture permanente des sockets et des ressources jusqu'à un crash (plus de scénario de ressources) ou un redémarrage.
Arun Joshla

Réponses:

196

Lorsque vous avez terminé d'utiliser votre Connection, vous devez le fermer explicitement en appelant sa close()méthode afin de libérer toutes les autres ressources de base de données (curseurs, poignées, etc.) sur lesquelles la connexion peut se maintenir.

En fait, le modèle sûr en Java est de fermer votre ResultSet, Statementet Connection(dans cet ordre) dans un finallybloc lorsque vous en avez terminé avec eux, quelque chose comme ça:

Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;

try {
    // Do stuff
    ...

} catch (SQLException ex) {
    // Exception handling stuff
    ...
} finally {
    if (rs != null) {
        try {
            rs.close();
        } catch (SQLException e) { /* ignored */}
    }
    if (ps != null) {
        try {
            ps.close();
        } catch (SQLException e) { /* ignored */}
    }
    if (conn != null) {
        try {
            conn.close();
        } catch (SQLException e) { /* ignored */}
    }
}

Le finallybloc peut être légèrement amélioré en (pour éviter le contrôle nul):

} finally {
    try { rs.close(); } catch (Exception e) { /* ignored */ }
    try { ps.close(); } catch (Exception e) { /* ignored */ }
    try { conn.close(); } catch (Exception e) { /* ignored */ }
}

Mais, quand même, c'est extrêmement verbeux, vous finissez généralement par utiliser une classe d'assistance pour fermer les objets dans des méthodes d'assistance nulles et le finallybloc devient quelque chose comme ça:

} finally {
    DbUtils.closeQuietly(rs);
    DbUtils.closeQuietly(ps);
    DbUtils.closeQuietly(conn);
}

Et, en fait, Apache Commons DbUtils a une DbUtilsclasse qui fait précisément cela, il n'est donc pas nécessaire d'écrire la vôtre.

Pascal Thivent
la source
3
Super aide, merci! Je n'ai pas attrapé ni pensé aux instructions conn! = Null.
onaclov2000
1
@ onaclov2000 Oui, rs, ps, connpeut être en nullfonction de l' endroit où les pauses de code. C'est pourquoi c'est ce qu'on appelle le modèle «sûr».
Pascal Thivent
12
@Pascal Thivent: En fait, nous n'avons pas besoin de les fermer tous. Le livre "Core Java Volume two - Advanced Features" a écrit: La closeméthode d'un Statementobjet ferme automatiquement l'associé ResultSetsi l'instruction a un jeu de résultats ouvert. De même, la closeméthode de la Connectionclasse se ferme tout Statementsde Connection.
Majid Azimi
12
@Majid: Sauf s'il s'agit d'une connexion groupée. Les déclarations fuiraient alors.
BalusC
1
@BalusC: Pouvez-vous expliquer ce qui se passe lorsqu'une connexion groupée est fermée en utilisant la méthode connection.close ()
Krsna Chaitanya
61

Il est toujours préférable de fermer les objets de base de données / ressources après utilisation. Il est préférable de fermer les objets de connexion, de jeu de résultats et d'instruction dans le finallybloc.

Jusqu'à Java7, toutes ces ressources doivent être fermées à l'aide d'un finallybloc. Si vous utilisez Java 7, pour fermer les ressources, vous pouvez procéder comme suit.

try(Connection con = getConnection(url, username, password, "org.postgresql.Driver");
    Statement stmt = con.createStatement();
    ResultSet rs = stmt.executeQuery(sql);
) {

//statements
}catch(....){}

Désormais, les objets con, stmt et rs font partie du bloc try et java ferme automatiquement ces ressources après utilisation.

J'espère que j'ai été utile.

Yadu Krishnan
la source
Et si ma déclaration est implicite, c'est- ResultSet rs = conn.createStatement().executeQuery(sql);à- dire à l'intérieur du trybloc?
Antares42
1
Vous ne pourrez pas les référencer dans le bloc finally {} de clôture. Si une exception est levée, la méthode close () du ResultSet ne sera jamais invoquée
Dan
Que se passe-t-il si je ne les ferme pas?
Alex78191
si vous ne les fermez pas, des fuites de mémoire peuvent se produire.
Yadu Krishnan
14

Il suffit de fermer juste Statementet Connection. Il n'est pas nécessaire de fermer explicitement l' ResultSetobjet.

La documentation Java parle de java.sql.ResultSet:

Un objet ResultSet est automatiquement fermé par l'objet Statement qui l'a généré lorsque cet objet Statement est fermé, réexécuté ou utilisé pour récupérer le résultat suivant à partir d'une séquence de résultats multiples.


Merci BalusC pour les commentaires: "Je ne me fierais pas à cela. Certains pilotes JDBC échouent là-dessus."

Grigori A.
la source
25
Je ne me fierais pas à ça. Certains pilotes JDBC échouent là-dessus. Par exemple, Oracle avec "Curseurs ouverts maximum dépassés", etc. Fermez simplement explicitement toutes les ressources ouvertes, sans excuses.
BalusC
1
Je préférerais alors ne pas utiliser de pilotes non conformes aux spécifications
Enerccio
2
Comme le souligne BalusC, c'est une bonne programmation défensive de fermer explicitement la connexion au lieu de câbler une dépendance à un fournisseur particulier.
michaelok le
11

Oui. Vous devez fermer l'ensemble de résultats, l'instruction et la connexion. Si la connexion provient d'un pool, sa fermeture la renvoie en fait au pool pour une réutilisation.

Vous devez généralement le faire dans un finally{}bloc, de sorte que si une exception est levée, vous avez toujours la possibilité de la fermer.

De nombreux frameworks s'occuperont de ce problème d'allocation / désallocation des ressources pour vous. par exemple le JdbcTemplate de Spring . Apache DbUtils a des méthodes pour s'occuper de la fermeture du jeu de résultats / de l'instruction / de la connexion, qu'elle soit nulle ou non (et intercepter les exceptions à la fermeture), ce qui peut également aider.

Brian Agnew
la source
1
Quand j'insère une éclipse "enfin" aime la mettre en évidence en me disant que c'est faux. cela devrait-il aller après les blocs catch?
onaclov2000
Oui. essayez {} catch {} enfin {}. Le catch {} est facultatif, btw. Tout comme le finalement {}
Brian Agnew
J'ai déplacé les instructions "close" vers la fin, mais elles disent simplement "sqlexception", des suggestions?
onaclov2000
1
close () lève une exception SQLException. Vous devez gérer cela. Voir DbUtils.closeQuietly () pour gérer cela en silence.
Brian Agnew
> Que se passe-t-il vraiment si conn.close () ne se produit pas?
Alex78191
8

En fait, il est préférable d'utiliser un bloc try-with-resources et Java fermera toutes les connexions pour vous lorsque vous quitterez le bloc try.

Vous devez le faire avec tout objet qui implémente AutoClosable.

try (Connection connection = getDatabaseConnection(); Statement statement = connection.createStatement()) {
    String sqlToExecute = "SELECT * FROM persons";
    try (ResultSet resultSet = statement.execute(sqlToExecute)) {
        if (resultSet.next()) {
            System.out.println(resultSet.getString("name");
        }
    }
} catch (SQLException e) {
    System.out.println("Failed to select persons.");
}

L'appel à getDatabaseConnection est juste composé. Remplacez-le par un appel qui vous permet d'obtenir une connexion SQL JDBC ou une connexion à partir d'un pool.

Joe
la source
Vous n'avez donc pas à fermer manuellement la connexion dans ce cas?
Colin D
1
Correct. Vous n'êtes pas obligé de fermer explicitement la connexion. Il sera fermé lorsque la fin du bloc de code d'essai sera atteinte.
Joe
7

Oui, vous devez fermer la connexion. Sinon, le client de base de données gardera généralement la connexion socket et les autres ressources ouvertes.

Alex Miller
la source
... jusqu'à ce qu'il sorte. Cela attache diverses ressources finies côté client et côté serveur. Si un client fait trop ce genre de chose, cela peut causer des problèmes au client lui-même, au service de base de données et peut-être même à d'autres applications s'exécutant sur une machine client ou serveur.
Stephen C