J'ai une méthode pour obtenir des utilisateurs d'une base de données avec JDBC:
public List<User> getUser(int userId) {
String sql = "SELECT id, name FROM users WHERE id = ?";
List<User> users = new ArrayList<User>();
try {
Connection con = DriverManager.getConnection(myConnectionURL);
PreparedStatement ps = con.prepareStatement(sql);
ps.setInt(1, userId);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
users.add(new User(rs.getInt("id"), rs.getString("name")));
}
rs.close();
ps.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
Comment utiliser Java 7 try-with-resources pour améliorer ce code?
J'ai essayé avec le code ci-dessous, mais il utilise de nombreux try
blocs et n'améliore pas beaucoup la lisibilité . Dois-je utiliser try-with-resources
d'une autre manière?
public List<User> getUser(int userId) {
String sql = "SELECT id, name FROM users WHERE id = ?";
List<User> users = new ArrayList<>();
try {
try (Connection con = DriverManager.getConnection(myConnectionURL);
PreparedStatement ps = con.prepareStatement(sql);) {
ps.setInt(1, userId);
try (ResultSet rs = ps.executeQuery();) {
while(rs.next()) {
users.add(new User(rs.getInt("id"), rs.getString("name")));
}
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
try (ResultSet rs = ps.executeQuery()) {
car un objet ResultSet est automatiquement fermé par l'objet Statement qui l'a généréConnection
,PreparedStatement
etResultSet
aussi. Aucune raison de ne pas vraiment le faire, car l'essai avec des ressources le rend si facile et rend notre code plus auto-documenté quant à nos intentions.Réponses:
Il n'y a pas besoin d'essayer externe dans votre exemple, donc vous pouvez au moins passer de 3 à 2, et vous n'avez pas non plus besoin de fermer
;
à la fin de la liste des ressources. L'avantage d'utiliser deux blocs try est que tout votre code est présent à l'avance, vous n'avez donc pas à vous référer à une méthode distincte:la source
Connection::setAutoCommit
? Un tel appel n'est pas autorisétry
entre lecon =
et leps =
. Lors de l'obtention d'une connexion à partir d'un DataSource qui peut être sauvegardé avec un pool de connexions, nous ne pouvons pas supposer comment autoCommit est défini.DriverManager.getConnection(myConnectionURL)
à une méthode qui définit également l'indicateur autoCommit et retourne la connexion (ou la définir dans l'équivalent de lacreatePreparedStatement
méthode de l'exemple précédent ...)DataSource
endroit où lagetConnection
méthode fait ce que vous dites, obtenez la connexion et configurez-la si nécessaire, puis transmettez la connexion.Je me rends compte que cela a été répondu il y a longtemps, mais je veux suggérer une approche supplémentaire qui évite le double bloc try-with-resources imbriqué.
la source
createPreparedStatement
est dangereux quelle que soit la façon dont vous l'utilisez. Pour résoudre ce problème, vous devez ajouter un try-catch autour de setInt (...), en attraperSQLException
, et quand cela se produit, appelez ps.close () et relancez l'exception. Mais cela aboutirait à un code presque aussi long et peu élégant que le code que l'OP voulait améliorer.Voici une manière concise d'utiliser lambdas et le fournisseur JDK 8 pour tout adapter à l'essai extérieur:
la source
Qu'en est-il de la création d'une classe wrapper supplémentaire?
Ensuite, dans la classe appelante, vous pouvez implémenter la méthode prepareStatement comme:
la source
Comme d'autres l'ont indiqué, votre code est fondamentalement correct bien que l'extérieur ne
try
soit pas nécessaire. Voici quelques réflexions supplémentaires.DataSource
D'autres réponses ici sont correctes et bonnes, comme la réponse acceptée par bpgergo. Mais aucun des exemples ne montre l'utilisation de
DataSource
, généralement recommandée sur l'utilisation deDriverManager
Java moderne.Par souci d'exhaustivité, voici un exemple complet qui récupère la date actuelle sur le serveur de base de données. La base de données utilisée ici est Postgres . Toute autre base de données fonctionnerait de la même manière. Vous remplaceriez l'utilisation de
org.postgresql.ds.PGSimpleDataSource
par une implémentationDataSource
appropriée à votre base de données. Une implémentation est probablement fournie par votre pilote ou pool de connexions si vous suivez cette voie.Une
DataSource
implémentation n'a pas besoin d' être fermée, car elle n'est jamais «ouverte». ADataSource
n'est pas une ressource, n'est pas connecté à la base de données, il ne contient donc pas de connexions réseau ni de ressources sur le serveur de base de données. ADataSource
est simplement des informations nécessaires lors de la connexion à la base de données, avec le nom ou l'adresse réseau du serveur de base de données, le nom d'utilisateur, le mot de passe de l'utilisateur et diverses options que vous souhaitez spécifier lorsqu'une connexion est finalement établie. Ainsi, votreDataSource
objet d'implémentation ne rentre pas dans vos parenthèses try-with-resources.Try-with-resources imbriqué
Votre code utilise correctement les instructions try-with-resources imbriquées.
Notez dans l'exemple de code ci-dessous que nous utilisons également la syntaxe try-with-resources deux fois , l'une imbriquée dans l'autre. L'extérieur
try
définit deux ressources:Connection
etPreparedStatement
. L'intérieurtry
définit laResultSet
ressource. Il s'agit d'une structure de code commune.Si une exception est levée depuis l'intérieur, et n'y est pas interceptée, la
ResultSet
ressource sera automatiquement fermée (si elle existe, elle n'est pas nulle). Ensuite, lePreparedStatement
sera fermé, et enfin leConnection
sera fermé. Les ressources sont automatiquement fermées dans l'ordre inverse dans lequel elles ont été déclarées dans les instructions try-with-resource.L'exemple de code ici est trop simpliste. Tel qu'il est écrit, il peut être exécuté avec une seule instruction try-with-resources. Mais dans un vrai travail, vous ferez probablement plus de travail entre la paire d'
try
appels imbriqués . Par exemple, vous pouvez extraire des valeurs de votre interface utilisateur ou d'un POJO, puis les transmettre pour remplir les?
espaces réservés dans votre SQL via des appels à desPreparedStatement::set…
méthodes.Notes de syntaxe
Point-virgule de fin
Notez que le point-virgule à la fin de la dernière instruction de ressource entre les parenthèses du try-with-resources est facultatif. Je l'inclus dans mon propre travail pour deux raisons: La cohérence et il semble complet, et cela facilite le copier-coller d'un mélange de lignes sans avoir à se soucier des points-virgules de fin de ligne. Votre IDE peut marquer le dernier point-virgule comme superflu, mais il n'y a aucun mal à le laisser.
Java 9 - Utiliser les variables existantes dans try-with-resources
La nouveauté de Java 9 est une amélioration de la syntaxe try-with-resources. Nous pouvons maintenant déclarer et remplir les ressources en dehors des parenthèses de l'
try
instruction. Je n'ai pas encore trouvé cela utile pour les ressources JDBC, mais gardez cela à l'esprit dans votre propre travail.ResultSet
devrait se fermer, mais ne peut pasDans un monde idéal, le
ResultSet
se fermerait comme le promet la documentation:Malheureusement, dans le passé, certains pilotes JDBC n'ont pas tenu cette promesse. En conséquence, de nombreux programmeurs JDBC ont appris à fermer explicitement toutes leurs ressources JDBC , y compris
Connection
,PreparedStatement
etResultSet
aussi. La syntaxe moderne try-with-resources a rendu cela plus facile et avec un code plus compact. Notez que l'équipe Java s'est donnée la peine de marquerResultSet
commeAutoCloseable
, et je suggère que nous nous en servions. L'utilisation d'un essai avec des ressources autour de toutes vos ressources JDBC rend votre code plus auto-documenté quant à vos intentions.Exemple de code
la source