java - una - Cómo cerrar correctamente los recursos
como cerrar un jframe en java (6)
Mientras estaba limpiando un poco de código, FindBugs me señaló un poco del código JDBC que usa los objetos Connection, CallableStatement y ResultSet. Aquí hay un fragmento de ese código:
CallableStatement cStmt = getConnection().prepareCall("...");
...
ResultSet rs = cStmt.executeQuery();
while ( rs.next() )
{
...
}
cStmt.close();
rs.close();
con.close();
FindBugs señaló que estos deberían estar dentro de un bloque finally. Empecé a refactorizar mi código para hacer esto y empecé a preguntarme cómo manejar el código dentro del bloque finally.
Es posible que la creación de los objetos CallableStatement of Connection genere una excepción, dejando mi objeto ResultSet como nulo. Cuando trato de cerrar el ResultSet, obtengo una NullPointerException y mi Connection, a su vez, nunca se cierra. De hecho, este hilo trae el mismo concepto y muestra que envolver sus invocaciones close () en un cheque nulo es una buena idea.
¿Pero qué pasa con otras posibles excepciones? De acuerdo con la especificación API de Java, Statement.close () puede arrojar una SQLException "si se produce un error en la base de datos". Entonces, incluso si mi CallableStatement no es nulo y puedo invocar con éxito a close () en él, aún podría obtener una excepción y no tener la oportunidad de cerrar mis otros recursos.
La única solución "a prueba de errores" en la que puedo pensar es ajustar cada invocación de close () en su propio bloque try / catch, como este:
finally {
try {
cStmt.close();
} catch (Exception e) { /* Intentionally Swallow Exception */ }
try {
rs.close();
} catch (Exception e) { /* Intentionally Swallow Exception */ }
try {
con.close();
} catch (Exception e) { /* Intentionally Swallow Exception */ }
}
Chico, si eso no se ve horrible. ¿Hay una mejor manera de hacerlo?
Bueno, básicamente eso es lo que haces, excepto que antes que nada no necesariamente tragas la excepción (puedes anular la comprobación y, al menos, LOGAR la excepción). En segundo lugar, puede configurar una buena clase de utilidad con cosas como
public static void close(ResultSet rs) {
try { if (rs != null) rs.close();
} catch (SQLException (e) {
log.error("",e);
}
}
Entonces solo estática importa esa clase.
tu finalmente se convierte en algo así como
finally {
close(resultset);
close(statement);
close(connection);
}
que realmente no es tan horrible.
Creo que la mejor respuesta ya se ha mencionado, pero pensé que podría ser interesante mencionar que podría considerar la nueva función JDK 7 de recursos que se pueden cerrar automáticamente.
try{
try(Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/hrdb", "obiwan", "kenobi");
Statement stm = conn.createStatement();
ResultSet rs = stm.executeQuery("select name from department")) {
while(rs.next()){
System.out.println(rs.getString("name"));
}
}
}catch(SQLException e){
//you might wanna check e.getSuppressed() as well
//log, wrap, rethrow as desired.
}
No todos nosotros podemos migrar a JDK 7 ahora, pero para aquellos que pueden comenzar a jugar con la vista previa del desarrollador, esta ofrece una forma interesante de hacer las cosas y ciertamente puede desaprobar otros enfoques en el futuro cercano.
La única manera que conozco de ocultar todo ese feo código repetitivo es utilizar algo como la Plantilla JBDC de Spring .
Puedes envolver un bloque en otro bloque:
try{
Connection c = ...
try{
statement = c.prepareStatement(...);
try{
rs = statement.execute(...);
}finally{
rs.close();
}
}finally{
statement.close()
}
}finally{
c.close();
}
}catch(SQLException e){}
Usa el bloque de captura más bajo para todo lo que pueda surgir
Usa la limpieza de Lombok , si puedes:
@Cleanup
Connection c = ...
@Cleanup
statement = c.prepareStatement(...);
@Cleanup
rs = statement.execute(...);
Este trabajo se traduce en tres bloques try-finally anidados y funciona bien con excepción. ¡Nunca se trague una excepción sin una muy buena razón!
Una alternativa:
Escriba un método de utilidad propio como este:
public static void close(ResultSet rs, Statement stmt, Connection con) throws SQLException {
try {
try {
if (rs!=null) rs.close();
} finally {
if (stmt!=null) stmt.close();
}
} finally {
if (con!=null) con.close();
}
}
y usarlo en
try {
Connection con = ...
Statement stmt = ...
ResultSet rs = ...
} finally {
close(rs, stmt, con);
}
y deje que la Excepción salte o maneje como lo desee.
Solo debe cerrar la Conexión.
try
{
cStmt.close();
}
catch(Exception e)
{
/* Intentionally Swallow Exception */
}
De docs.oracle.com:
Un objeto Statement se cierra automáticamente cuando se recolecta basura. Cuando se cierra un objeto Statement, su objeto actual ResultSet, si existe, también se cierra.
Llamar a close () en una conexión libera su base de datos y recursos JDBC.