121 votes

Fermeture des connexions JDBC dans le pool

Notre section de code standard pour l'utilisation de JDBC est...

Connection conn = getConnection(...);
Statement  stmt = conn.conn.createStatement (ResultSet.TYPE_SCROLL_INSENSITIVE,
                                                ResultSet.CONCUR_READ_ONLY);
ResultSet  rset = stmt.executeQuery (sqlQuery);

// do stuff with rset

rset.close(); stmt.close(); conn.close();

Question 1 : Lorsque l'on utilise le pool de connexion, doit-on fermer la connexion à la fin ? Si c'est le cas, le but de la mise en commun n'est-il pas perdu ? Et sinon, comment la source de données sait-elle quand une instance particulière de connexion est libérée et peut être réutilisée ? Je suis un peu perdu sur ce point, toute indication sera appréciée.

Question 2 : La méthode suivante est-elle proche de la norme ? On dirait une tentative d'obtenir une connexion à partir du pool et, si la source de données ne peut être établie, d'utiliser le bon vieux DriverManager. Nous ne sommes même pas sûrs de la partie qui est exécutée au moment de l'exécution. Je répète la question précédente : doit-on fermer la connexion qui sort d'une telle méthode ?

Merci, - MS.

synchronized public Connection getConnection (boolean pooledConnection)
                                                        throws SQLException {
        if (pooledConnection) {
                if (ds == null) {
                        try {
                                Context envCtx = (Context)
                                        new InitialContext().lookup("java:comp/env");
                                ds = (DataSource) envCtx.lookup("jdbc/NamedInTomcat");
                                return ds.getConnection();
                        } catch (NamingException e) {
                                e.printStackTrace();
                }}
                return (ds == null) ? getConnection (false) : ds.getConnection();
        }
        return DriverManager.getConnection(
                "jdbc:mysql://"+ipaddy+":"+dbPort +"/" + dbName, uName, pWord);
}

Edit : Je pense que nous obtenons la connexion mutualisée puisque nous ne voyons pas de trace de pile.

135voto

BalusC Points 498232

Lorsqu'on utilise le pool de connexion, doit-on fermer la connexion à la fin ? Si c'est le cas, le but de la mise en commun n'est-il pas perdu ? Et sinon, comment la source de données sait-elle quand une instance particulière de connexion est libérée et peut être réutilisée ? Je suis un peu perdu sur ce point, toute indication sera appréciée.

Oui, il est certain que vous devez également fermer la connexion mutualisée. C'est en fait une enveloppe autour de la connexion réelle. Il libère sous le manteau la connexion réelle vers le pool. C'est ensuite au pool de décider si la connexion réelle sera en fait être fermé ou être réutilisé pour une nouvelle getConnection() appel. Donc, que vous utilisiez un pool de connexion ou non, vous devez toujours fermez toutes les ressources JDBC dans l'ordre inverse dans le fichier finally du bloc try bloc où vous les avez acquis. Dans Java 7, cela peut être encore simplifié en utilisant try-with-resources déclaration.


La méthode suivante est-elle proche de la norme ? On dirait une tentative d'obtenir une connexion à partir du pool et, si la source de données ne peut être établie, d'utiliser le bon vieux DriverManager. Nous ne sommes même pas sûrs de la partie qui est exécutée au moment de l'exécution. Je répète la question précédente : doit-on fermer la connexion qui sort d'une telle méthode ?

L'exemple est assez effrayant. Vous avez juste besoin de rechercher/initialiser la fonction DataSource une seule fois pendant le démarrage de l'application dans un constructeur / initialisation d'une classe de configuration de la base de données pour toute l'application. Ensuite, il suffit d'appeler getConnection() sur une seule et même source de données pendant toute la durée de vie de l'application. Pas besoin de synchronisation ni de contrôles nuls.

Voir aussi :

0 votes

C'est ce qu'il fait (initialiser une fois), n'est-ce pas ? ds est une variable d'instance, et if (ds == null) ... est la partie initialisation.

0 votes

Effectuer les contrôles chaque fois dans une méthode de récupération comme getConnection() est bizarre. Faites-le simplement dans le bloc d'initialisation de la même classe, sans synchronisation ni contrôle à vide. Il ne sera appelé qu'une seule fois. Pour plus d'astuces et d'exemples de démarrage, vous pouvez trouver cet article utile.

0 votes

Excellent article, BalusC. La classe dont je m'occupe implémente plus ou moins la couche de données, en utilisant des DTO. Je suis d'accord avec vous, l'initialisation devrait être dans le constructeur. Maintenant, cette classe a une tonne de méthodes, chacune avec conn, stmt et rset comme variables locales, les connexions sont dans un bloc try, et finalement il y a un appel d'une ligne csrClose (conn, stmt, rset), où les 3 sont fermés (dans l'ordre inverse). Maintenant, le DTO que vous développez dans l'exemple est une image miroir d'une ligne de table de la DB. Nous avons des requêtes SQL complexes avec des jointures (et d'autres clauses), avez-vous un article sur la façon de développer des DAO pour de tels résultats ?

24voto

taer Points 321

Les pools vous renvoient généralement un objet Connexion enveloppé, où la méthode close() est surchargée, renvoyant généralement la Connexion au pool. L'appel de close() est acceptable et probablement toujours nécessaire.

Une méthode close() ressemblerait probablement à ceci :

public void close() throws SQLException {
  pool.returnConnection(this);
}

Pour votre deuxième question, vous pourriez ajouter un enregistreur pour indiquer si le bloc inférieur fonctionne. J'imagine cependant que vous ne voudriez que l'un ou l'autre pour la configuration de vos connexions à la base de données. Nous utilisons uniquement un pool pour nos accès aux bases de données. Dans tous les cas, il est important de fermer la connexion pour éviter les fuites.

0 votes

Je suis d'accord, nous avons un logger, et il pourrait être utilisé ici aussi. Je dois étudier un peu comment on peut envelopper un objet, surcharger sa méthode close() tout en gardant le même nom de classe (Connection).

1 votes

Calling close() is OK and probably still required. si vous n'appelez pas close, la connexion sera perdue, à moins que le pool ne mette en œuvre une stratégie de récupération.

0voto

user1588303 Points 173

En fait, la meilleure approche de la gestion des connexions est de ne pas les confier à un code quelconque.

Créez une classe SQLExecutor qui est le seul et unique emplacement qui ouvre et ferme les connexions.

Tout le reste de l'application pompe alors des instructions dans l'exécuteur au lieu d'obtenir des connexions du pool et de les gérer (ou de les mal gérer) partout.

Vous pouvez avoir autant d'instances de l'exécuteur que vous le souhaitez, mais personne ne doit écrire de code qui ouvre et ferme des connexions en son nom propre.

De façon pratique, cela vous permet également d'enregistrer tout votre SQL à partir d'un seul ensemble de code.

0 votes

Cette idée semble raisonnable, du moins pour certaines applications, mais elle n'est que peu liée à la question posée ici. Vous répondez probablement à la question 2 en particulier, mais même dans ce cas, votre approche prescrite n'exclut pas l'utilisation des connexions, mais seulement en dehors de cette classe SQLExecutor. Vous pourriez donc considérer la question dans le contexte de votre classe SQLExecutor.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X