2008-09-19 50 views
49

que he estado haciendo revisión de código (en su mayoría utilizando herramientas como FindBugs) de uno de nuestros proyectos favoritos y FindBugs marcada como errónea siguiente código (pseudocódigo):ResultSet no cerrado cuando se cierra la conexión?

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 . Me di cuenta de que el conjunto de resultados y la declaración no estaban cerradas, así que les cerraba en último:

finally{ 
    try{ 
     rs.close() 
    }catch(SqlException se){ 
     //log it 
    } 
    try{ 
     stmt.close(); 
    }catch(SqlException se){ 
     //log it 
    } 
    conn.close(); 
} 

Pero me encontré con el patrón anterior en muchos proyectos (de un buen número de empresas), y nadie estaba ResultSets cierre o Declaraciones.

¿Tuvo problemas con ResultSets y las declaraciones no se cierran cuando la conexión está cerrada?

Encontré solo this y se refiere a que Oracle tiene problemas para cerrar los ResultSets al cerrar Connections (usamos Oracle db, de ahí mis correcciones). java.sql.api no dice nada en Connection.close() javadoc.

+0

Le recomiendo usar Apache Commons-dbUtils (http://commons.apache.org/dbutils/) Es una biblioteca JDBC ligero que realmente limpia un montón de código estándar JDBC. –

+2

Este es el tipo de errores que se producen cuando no cierran los objetos relevantes: "ORA-01000: el máximo de los cursores abiertos excedió" - http: // stackoverflow.com/questions/12192592/java-sqlception-ora-01000-maximum-open-cursors-excedido –

+0

cursor de la base de datos - http://stackoverflow.com/questions/3861558/what-are-the-benefits-of-using -database-cursor Un cursor es una herramienta que le permite iterar los registros en un conjunto. Tiene conceptos de orden y registro actual. –

Respuesta

51

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 utiliza la agrupación de conexiones, el 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.

+14

El método javadocs for Connection # close dice que "libera la base de datos de este objeto Connection y los recursos JDBC de inmediato". Creo que el problema es que algunas implementaciones malas no hacen el trabajo correcto. Cuando las agrupaciones no cierran los recursos relacionados, ¿están haciendo lo incorrecto? – marcospereira

+2

La mayoría de los controladores JDBC de los principales proveedores corresponden a las especificaciones. Sin embargo, la mayoría (si no todos) los servidores de aplicaciones mantienen un grupo de conexiones. Envuelven la conexión nativa y métodos de reaplicación como close() para que las conexiones puedan 'agruparse'. Esto significa que si trabaja en estos entornos, DEBE cerrar recursos como Declaraciones y ResultSets usted mismo. –

+4

@Ryan Fernandes: Bueno, algunos grupos solo le dan un objeto connectionProxy que guarda todas las declaraciones abiertas en él. Cuando se devuelve en el grupo, cierra todas las declaraciones abiertas. –

27

Tuve problemas con los ResultSets no cerrados en Oracle, aunque la conexión estaba cerrada. El error que me dieron fue

"ORA-01000: maximum open cursors exceeded" 

Así: Cierre siempre el conjunto de resultados!

+1

+1 - por mencionar las consecuencias de no cerrar los recursos. –

8

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 la reutilización de un comunicado se cerrará ningún resultado asociado abiertos, y el cierre de una declaración se cerrará ningún resultado asociado, pero no veo nada sobre el cierre de una conexión se cerrará cualquiera de los recursos creó.

Todos estos 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

+0

_pero no veo nada sobre el cierre de una conexión cerrará ninguno de los recursos que creó._ http://docs.oracle.com/javase/6/docs/api/java/sql/Connection.html#close() _ libera la base de datos de este objeto Connection ** y los recursos JDBC ** inmediatamente_ Pero sí, siempre es mejor cerrar todo. – user454322

4

Definitivamente he visto problemas con los ResultSets no cerrados, y qué puede doler cerrarlos todo el tiempo, ¿verdad? 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 para usted. Puede que no sea factible en su entorno de desarrollo, pero he tenido la gran suerte de utilizar Spring para administrar las transacciones JPA. Los detalles desordenados de la apertura de conexiones, declaraciones, conjuntos de resultados y la escritura demasiado complicada try/catch/finalmente bloquea (con try/catch bloquea en el bloque finally!) para volver a cerrarlos simplemente desaparece, dejándote a reall y obtener un trabajo hecho. Recomiendo migrar a ese tipo de solución.

+1

Es un punto en esta aplicación. Usamos EJB, y esta parte era una especie de complemento: nuestro cliente implementaría un origen de datos en Serwer y luego podría consultarlo a través de este complemento. –

4

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 un motivo para invocarlo, e ignorarlo lo hace bajo su propio riesgo.

8

El puente ODBC puede producir una pérdida de memoria con algunos controladores ODBC.

Si utiliza un buen controlador JDBC, entonces no debería tener problemas para cerrar la conexión. Pero hay 2 problemas:

  • ¿Sabe si tiene un buen controlador?
  • ¿Va a utilizar otros controladores JDBC en el futuro?

Que la mejor práctica es cerrarlo todo.

19

Siempre debe cerrar todos los recursos JDBC explícitamente. 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í es un método de utilidad que se puede utilizar a partir de 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) { 
     } 
    } 
} 
+6

Hombre, tal vez necesitamos una interfaz de cierre, ¿eh? – marcospereira

+2

ResultSet se cierran automáticamente cuando cierra el extracto. (Consulte JavaDoc http://download.oracle.com/javase/1.4.2/docs/api/java/sql/Statement.html) –

+0

Gracias por publicar. Me gusta esta idea. – Abboq

8

trabajo en un entorno J2EE Web grande. 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 la siguiente manera:

  1. usuario podría solicitar la página
  2. Server se conecta a DB 1
  3. Selecciona Server en DB 1
  4. Server "cierra" la conexión al DB 1
  5. Server se conecta a DB 2
  6. ¡En punto muerto!

Esto ocurrió por 2 razones, experimentamos mucho más tráfico de lo 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 resolver esto, debe usar referencias de recursos en el archivo web.xml para sus Conexiones de base de datos y debe establecer el compartimiento de recursos compartidos 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> 
Cuestiones relacionadas