Où fermer Java PreparedStatements et les jeux de résultats?

Considérez le code:

PreparedStatement ps = null;
ResultSet rs = null;
try {
  ps = conn.createStatement(myQueryString);
  rs = ps.executeQuery();
  // process the results...
} catch (java.sql.SQLException e) {
  log.error("an error!", e);
  throw new MyAppException("I'm sorry. Your query did not work.");
} finally {
  ps.close();
  rs.close();
}

ce qui précède ne compile pas, car les deux PreparedStatement.close() et ResultSet.close() jettent un java.sql.SQLException . Est-ce que j'ajoute un bloc "essayer/attraper" à la clause "finalement"? Ou déplacer les déclarations dans la clause d'essai? Ou tout simplement pas la peine d'appeler à proximité?

34
demandé sur Tom Hawtin - tackline 2008-11-26 20:02:08

13 réponses

pour le fichier I/O, j'ajoute généralement un try / catch au bloc finish. Cependant, vous devez faire attention à ne pas jeter d'exceptions du bloc final, car elles causeront la perte de l'exception originale (si elle existe).

Voir cet article pour un exemple plus spécifique de connexion à la base de la clôture.

9
répondu Michael Myers 2008-11-26 17:09:30

en Java 7, vous ne devez pas les fermer explicitement, mais utilisez gestion automatique des ressources pour vous assurer que ressources sont fermées et que les exceptions sont gérées de manière appropriée. La manipulation d'Exception fonctionne comme ceci:

Exception in try | Exception in close | Result
-----------------+--------------------+----------------------------------------
      No         |        No          | Continue normally
      No         |        Yes         | Throw the close() exception
      Yes        |        No          | Throw the exception from try block
      Yes        |        Yes         | Add close() exception to main exception
                 |                    |  as "suppressed", throw main exception

J'espère que c'est logique. Permet le code pretty, comme ceci:

private void doEverythingInOneSillyMethod(String key)
  throws MyAppException
{
  try (Connection db = ds.getConnection()) {
    db.setReadOnly(true);
    ...
    try (PreparedStatement ps = db.prepareStatement(...)) {
      ps.setString(1, key);
      ...
      try (ResultSet rs = ps.executeQuery()) {
        ...
      }
    }
  } catch (SQLException ex) {
    throw new MyAppException("Query failed.", ex);
  }
}

avant Java 7, Il est préférable d'utiliser imbriqué enfin blocs, plutôt que de tester les références pour les nuls.

l'exemple que je vais montrer pourrait sembler laid avec la nidation profonde, mais en pratique, code bien conçu ne va probablement pas créer une connexion, déclaration, et les résultats tous dans la même méthode; souvent, chaque niveau de la nidation implique de passer une ressource à une autre méthode, qui l'utilise comme une usine pour une autre ressource. Avec cette approche, les exceptions d'un close() masqueront une exception de l'intérieur du try bloc. Cela peut être surmonté, mais il en résulte encore plus de code désordre, et nécessite une classe d'exception personnalisée qui fournit le chaînage d'exception "supprimé" présent dans Java 7.

Connection db = ds.getConnection();
try {
  PreparedStatement ps = ...;
  try {
    ResultSet rs = ...
    try {
      ...
    }
    finally {
      rs.close();
    }
  } 
  finally {
    ps.close();
  }
} 
finally {
  db.close();
}
42
répondu erickson 2013-09-24 22:50:12

si vous roulez vraiment à la main votre propre jdbc, il devient certainement salissant. Le close() à la enfin les besoins pour obtenir enveloppé avec ses propres try catch, qui, à tout le moins, c'est moche. Vous ne pouvez pas sauter la fermeture, bien que les ressources seront dégagées lorsque la connexion est fermée (ce qui pourrait ne pas être tout de suite, si vous utilisez une piscine). En fait, l'un des points de vente principaux de l'utilisation d'un cadre (par exemple hibernate) pour gérer votre accès à la base de données est de gérer la connexion et le jeu de résultats gérer pour ne pas oublier de fermer.

vous pouvez faire quelque chose de simple comme ceci, qui au moins cache le désordre, et garantit que vous n'oubliez pas quelque chose.

public static void close(ResultSet rs, Statement ps, Connection conn)
{
    if (rs!=null)
    {
        try
        {
            rs.close();

        }
        catch(SQLException e)
        {
            logger.error("The result set cannot be closed.", e);
        }
    }
    if (ps != null)
    {
        try
        {
            ps.close();
        } catch (SQLException e)
        {
            logger.error("The statement cannot be closed.", e);
        }
    }
    if (conn != null)
    {
        try
        {
            conn.close();
        } catch (SQLException e)
        {
            logger.error("The data source connection cannot be closed.", e);
        }
    }

}

et puis,

finally {
    close(rs, ps, null); 
}
25
répondu Steve B. 2011-07-20 19:24:51

ne perdez pas votre temps à coder la gestion des exceptions de bas niveau, utilisez une API de haut niveau comme Spring-JDBC, ou un wrapper personnalisé autour des objets connection/statement/rs, pour masquer le code Trial-catch compliqué.

7
répondu BraveSirFoobar 2008-11-26 17:20:03

à noter Également:

"Lorsqu'un objet Statement est fermé, son actuel objet ResultSet, s'il existe, est également fermé. "

http://java.sun.com/j2se/1.5.0/docs/api/java/sql/Statement.html#close ()

il devrait suffire de ne refermer que la déclaration préparée en fin de Compte, et seulement si elle n'est pas déjà fermée. Si vous voulez être vraiment particulier cependant, fermez le jeu de résultats D'abord, pas après fermer L'État de préparation (le fermer après, comme certains des exemples ici, devrait en fait garantir une exception, puisqu'il est déjà fermé).

7
répondu 2009-03-25 15:13:20

j'ai habituellement une méthode utilitaire qui peut fermer des choses comme ça, y compris en prenant soin de ne pas essayer de faire quoi que ce soit avec une référence nulle.

habituellement si close() jette une exception que je ne me soucie pas réellement, donc je juste enregistrer l'exception et l'avaler - mais une autre alternative serait de le convertir en un RuntimeException . Quoi qu'il en soit, je recommande de le faire dans une méthode utilitaire qui est facile à appeler, car vous pourriez bien avoir besoin de le faire dans de nombreux endroits.

notez que votre solution actuelle ne fermera pas le jeu de résultats si la fermeture du prepare Statement échoue - il est préférable d'utiliser des blocs finalement imbriqués.

5
répondu Jon Skeet 2008-11-26 17:11:23

si vous utilisez Java 7, vous pouvez utiliser les améliorations des mécanismes de gestion des exceptions dans les classes qui implémentent Autoclosable (i.e. PreparedStatement , Resultset )

vous pourriez également trouver cette question intéressante: jeu de résultats de fermeture en Java 7

3
répondu Guido 2017-05-23 12:17:54

je sais que c'est une vieille question, mais juste au cas où quelqu'un cherche la réponse, java a maintenant la solution d'essai-avec-ressources.

static String readFirstLineFromFile(String path) throws IOException {
      try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}
1
répondu Xin 2013-06-12 05:02:03

n'omettez pas d'appeler de près. Il peut causer des problèmes.

je préfère ajouter le bloc "essayer/attraper" à la fin.

0
répondu asalamon74 2008-11-26 17:10:13

probablement une vieille (bien que simple) façon de faire les choses, mais il fonctionne toujours:

public class DatabaseTest {

    private Connection conn;    
    private Statement st;   
    private ResultSet rs;
    private PreparedStatement ps;

    public DatabaseTest() {
        // if needed
    }

    public String getSomethingFromDatabase(...) {
        String something = null;

        // code here

        try {
            // code here

        } catch(SQLException se) {
            se.printStackTrace();

        } finally { // will always execute even after a return statement
            closeDatabaseResources();
        }

        return something;
    }

    private void closeDatabaseResources() {
        try {
            if(conn != null) {
                System.out.println("conn closed");
                conn.close();
            }

            if(st != null) {
                System.out.println("st closed");
                st.close();
            }

            if(rs != null) {
                System.out.println("rs closed");
                rs.close();
            }

            if(ps != null) {
                System.out.println("ps closed");
                ps.close();
            }

        } catch(SQLException se) {
            se.printStackTrace();
        }               
    }
}
0
répondu silver 2014-12-08 05:43:50

en S'appuyant sur la réponse de @erickson, pourquoi ne pas simplement le faire dans un bloc try comme celui-ci?

private void doEverythingInOneSillyMethod(String key) throws MyAppException
{
  try (Connection db = ds.getConnection();
       PreparedStatement ps = db.prepareStatement(...)) {

    db.setReadOnly(true);
    ps.setString(1, key);
    ResultSet rs = ps.executeQuery()
    ...
  } catch (SQLException ex) {
    throw new MyAppException("Query failed.", ex);
  }
}

notez que vous n'avez pas besoin de créer l'objet ResultSet à l'intérieur du bloc try car ResultSet est automatiquement fermé lorsque l'objet PreparedStatement est fermé.

un objet ResultSet est automatiquement fermé lorsque l'objet Statement que généré, il est fermé, ré-exécuté, ou utilisé pour récupérer la prochaine résultat d'une séquence de résultats multiples.

référence: https://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html

0
répondu Catfish 2017-04-06 17:05:41

focus clause finally,

finally {
   try {
      rs.close();
      ps.close();
   } catch (Exception e) {
      // Do something
   }
}

je pense que vous devez modifier 2 points.

tout d'abord, utilisez try & catch à nouveau dans la clause fainlly.

en Second lieu, rs.close() avant de faire du ps.proche.)(

fly1997@naver.com

-1
répondu Kris 2012-08-06 08:06:38

j'utilise ceci..

finally
{
    if (ps != null) ps.close();
    if (rs != null) rs.close();
}
-2
répondu Chris Nava 2008-11-26 17:11:00