web-dev-qa-db-fra.com

Est-il sûr d'utiliser une instance Java.sql.Connection statique dans un système multithread?

J'exécute une application Web sur Tomcat. J'ai une classe qui gère toutes les requêtes DB. Cette classe contient l'objet Connection et les méthodes qui retournent les résultats de la requête.

Voici l'objet de connexion:

private static Connection conn = null;

Il n'a qu'une seule instance (singleton).

De plus, j'ai des méthodes qui exécutent des requêtes, telles que la recherche d'un utilisateur dans la base de données:

public static ResultSet searchUser(String user, String pass) throws SQLException

Cette méthode utilise l'objet statique Connection. Ma question est la suivante: mon utilisation dans le thread d'objet statique Connection est-elle sûre? Ou peut-il provoquer des problèmes lorsque de nombreux utilisateurs appellent la méthode searchUser?

48
Asher Saban

mon utilisation dans le thread d'objet de connexion statique est-elle sûre?

Absolument pas!

De cette façon, la connexion va être partagée entre toutes les demandes envoyées par tous les utilisateurs et donc toutes les requêtes interfèrent les unes avec les autres. Mais la sécurité des threads n'est pas votre seul problème, la fuite de ressources est également votre autre problème. Vous gardez une seule connexion ouverte pendant toute la durée de vie de l'application. La base de données moyenne récupère la connexion chaque fois qu'elle est ouverte depuis trop longtemps, ce qui se situe généralement entre 30 minutes et 8 heures, selon la configuration de la base de données. Donc, si votre application Web s'exécute plus longtemps, la connexion est perdue et vous ne pourrez plus exécuter de requêtes.

Ce problème s'applique également lorsque ces ressources sont conservées en tant que variable d'instance nonstatic d'une instance de classe qui est réutilisée plusieurs fois.

Vous devez toujours acquérir et fermer la connexion, l'instruction et l'ensemble de résultats dans le champ d'application le plus court possible , de préférence à l'intérieur du même try-with-resources block comme où vous exécutez la requête selon l'idiome JDBC suivant:

public User find(String username, String password) throws SQLException {
    User user = null;

    try (
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement("SELECT id, username, email FROM user WHERE username=? AND password=md5(?)");
    ) {
        statement.setString(1, username);
        statement.setString(2, password);

        try (ResultSet resultSet = statement.executeQuery()) {
            if (resultSet.next()) {
                user = new User();
                user.setId(resultSet.getLong("id"));
                user.setUsername(resultSet.getString("username"));
                user.setEmail(resultSet.getString("email"));
            }
        }
    }       

    return user;
}

Notez que vous ne devez pas renvoyer une ResultSet ici. Vous devez immédiatement le lire et le mapper à une classe non JDBC, puis le renvoyer, afin que la ResultSet puisse être fermée en toute sécurité.

Si vous n'êtes pas encore sur Java 7 encore, utilisez un try-finally bloc dans lequel vous fermez manuellement les ressources pouvant être fermées dans l'ordre inverse au fur et à mesure que vous les avez acquises. Vous pouvez trouver un exemple ici: À quelle fréquence la connexion, la déclaration et le ResultSet doivent-ils être fermés dans JDBC?

Si vous vous inquiétez des performances de connexion, vous devez plutôt utiliser le pool de connexions. Ceci est intégré dans de nombreux serveurs d'applications Java EE et même des serveurs de barebones comme Tomcat le prend en charge. Il suffit de créer une source de données JNDI dans le serveur lui-même et de laisser votre application Web la saisir en tant que DataSource. C'est déjà un pool de connexions transparent. Vous pouvez trouver un exemple dans le premier lien de la liste ci-dessous.

Voir également:

76
BalusC

Si vous exécutez uniquement des requêtes Select (searchUser ne semble que sélectionner des données), il n'y aura aucun problème, à l'exception de la contention des threads.

Pour autant que je sache, un Connection ne peut gérer qu'une seule requête à la fois, donc en utilisant une seule instance, vous sérialiserez essentiellement l'accès à la base de données. Mais cela ne signifie pas nécessairement, il est toujours sûr d'accéder à une base de données comme celle-ci dans un environnement multi-thread. Il peut toujours y avoir des problèmes si les accès simultanés s'entrelacent.

1
TPete