java - quality - sonar code review
ResultSet no se cerró cuando la conexión se cerró? (8)
Definitivamente he visto problemas con los ResultSets no cerrados, y ¿qué puede hacer para cerrarlos todo el tiempo? La falta de fiabilidad de tener que recordar hacer esto es una de las mejores razones para pasar a los marcos que gestionan estos detalles por usted. Puede que no sea factible en su entorno de desarrollo, pero he tenido la gran suerte de usar Spring para administrar las transacciones de JPA. Los detalles desordenados de abrir conexiones, declaraciones, conjuntos de resultados y escribir bloques de try / catch / finally demasiado complicados (con try / catch blocks en el bloque finally! ) Para cerrarlos de nuevo simplemente desaparecen, dejándote para que realices un trabajo. . Recomiendo migrar a ese tipo de solución.
He estado haciendo una revisión de código (principalmente usando herramientas como FindBugs) de uno de nuestros proyectos favoritos y FindBugs marcado con el siguiente código como erróneo (pseudocódigo):
Connection conn = dataSource.getConnection();
try{
PreparedStatement stmt = conn.prepareStatement();
//initialize the statement
stmt.execute();
ResultSet rs = stmt.getResultSet();
//get data
}finally{
conn.close();
}
El error fue que este código podría no liberar recursos. Descubrí que ResultSet y Statement no estaban cerrados, así que finalmente los cerré:
finally{
try{
rs.close()
}catch(SqlException se){
//log it
}
try{
stmt.close();
}catch(SqlException se){
//log it
}
conn.close();
}
Pero encontré el patrón anterior en muchos proyectos (de bastantes compañías), y nadie estaba cerrando ResultSets o declaraciones.
¿Tuvo problemas con ResultSets y declaraciones que no se cierran cuando se cierra la conexión?
Solo encontré this y se refiere a que Oracle tiene problemas para cerrar los ResultSets al cerrar Connections (utilizamos Oracle db, de ahí mis correcciones). java.sql.api no dice nada en Connection.close () javadoc.
El puente ODBC puede producir una pérdida de memoria con algunos controladores ODBC.
Si usa un buen controlador JDBC, entonces no debería tener problemas para cerrar la conexión. Pero hay 2 problemas:
- ¿Sabes si tienes un buen conductor?
- ¿Usará otros controladores JDBC en el futuro?
Que la mejor práctica es cerrarlo todo.
En Java, las declaraciones (no los conjuntos de resultados) se correlacionan con los cursores en Oracle. Lo mejor es cerrar los recursos que abra ya que puede haber un comportamiento inesperado con respecto a la JVM y los recursos del sistema.
Además, algunos marcos de agrupación JDBC agrupan enunciados y conexiones, por lo que no cerrarlos podría no marcar esos objetos como libres en el grupo y causar problemas de rendimiento en el marco.
En general, si hay un método close () o destroy () en un objeto, hay una razón para llamarlo, y para ignorarlo lo hace bajo su propio riesgo.
He tenido problemas con los ResultSets no cerrados en Oracle, aunque la conexión estaba cerrada. El error que obtuve fue
"ORA-01000: maximum open cursors exceeded"
Entonces: ¡siempre cierre su ResultSet!
Oracle le dará errores sobre los cursores abiertos en este caso.
De acuerdo con: http://java.sun.com/javase/6/docs/api/java/sql/Statement.html
parece que reutilizar una sentencia cerrará cualquier conjunto de resultados abierto, y cerrar una declaración cerrará cualquier conjunto de resultados, pero no veo nada sobre cómo cerrar una conexión que cerrará cualquiera de los recursos que creó.
Todos esos detalles se dejan al proveedor del controlador JDBC.
Siempre es más seguro cerrar todo explícitamente. Escribimos una clase de utilidades que envuelve todo con try {xxx} catch (Throwable {} para que pueda simplemente llamar a Utils.close (rs) y Utils.close (stmt), etc. sin tener que preocuparse por excepciones que supuestamente arrojan el análisis cercano .
Siempre debe cerrar todos los recursos JDBC de forma explícita. Como ya dijeron Aaron y John, cerrar una conexión a menudo solo la devolverá a un grupo y no todos los controladores JDBC se implementan exactamente del mismo modo.
Aquí hay un método de utilidad que puede usarse desde un bloque finally:
public static void closeEverything(ResultSet rs, Statement stmt,
Connection con) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
}
}
if (con != null) {
try {
con.close();
} catch (SQLException e) {
}
}
}
Trabajo en un gran entorno web J2EE. Tenemos varias bases de datos a las que se puede conectar en una sola solicitud. Comenzamos a tener bloqueos lógicos en algunas de nuestras aplicaciones. El problema era que de la siguiente manera:
- El usuario solicitaría la página
- El servidor se conecta a DB 1
- Servidor selecciona en DB 1
- El servidor "cierra" la conexión a la base de datos 1
- El servidor se conecta a DB 2
- ¡En punto muerto!
Esto ocurrió por 2 razones, experimentamos un volumen de tráfico mucho mayor que el normal y la especificación J2EE por defecto no cierra su conexión hasta que el hilo termina de ejecutarse. Por lo tanto, en el ejemplo anterior, el paso 4 nunca cerró la conexión a pesar de que finalmente se cerraron correctamente.
Para solucionarlo, debe usar las referencias de recursos en el archivo web.xml para las conexiones de la base de datos y debe establecer el compartimiento de la compartición de res como no utilizable.
Ejemplo:
<resource-ref>
<description>My Database</description>
<res-ref-name>jdbc/jndi/pathtodatasource</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Unshareable</res-sharing-scope>
</resource-ref>
Un problema con SÓLO el cierre de la conexión y no el conjunto de resultados, es que si su código de administración de conexión usa la agrupación de conexiones, connection.close()
simplemente volvería a poner la conexión en el grupo. Además, algunas bases de datos tienen un recurso de cursor en el servidor que no se liberará correctamente a menos que se cierre explícitamente.