Cerrar conexiones de bases de datos en Java

121

Me estoy confundiendo un poco, estaba leyendo lo siguiente de http://en.wikipedia.org/wiki/Java_Database_Connectivity

Connection conn = DriverManager.getConnection(
     "jdbc:somejdbcvendor:other data needed by some jdbc vendor",
     "myLogin",
     "myPassword" );

Statement stmt = conn.createStatement();
try {
    stmt.executeUpdate( "INSERT INTO MyTable( name ) VALUES ( 'my name' ) " );
} finally {
    //It's important to close the statement when you are done with it
    stmt.close();
}

¿No necesita cerrar la conexión conn? ¿Qué sucede realmente si no se produce el conn.close ()?

Tengo una aplicación web privada que mantengo que actualmente no cierra ninguno de los dos formularios, pero ¿es realmente importante el estándar, el estándar o ambos?

El sitio sigue cayendo de forma intermitente, pero el servidor sigue diciendo que es un problema de conexión de la base de datos, mi sospecha es que no se está cerrando, pero no sé cuál cerrar.

onaclov2000
fuente
Siempre es una buena práctica cerrar las conexiones por su cuenta, sin depender de otros controladores y plantillas para manejar el cierre. Si no se cierra la conexión, los sockets y los recursos se abrirán para siempre hasta un bloqueo (no más escenarios de recursos) o se reiniciarán.
Arun Joshla

Respuestas:

196

Cuando haya terminado de usar su Connection, debe cerrarlo explícitamente llamando a su close()método para liberar cualquier otro recurso de la base de datos (cursores, identificadores, etc.) a la que pueda estar reteniendo la conexión.

En realidad, el patrón seguro en Java es cerrar su ResultSet, Statementy Connection(en ese orden) en un finallybloque cuando haya terminado con ellos, algo así:

Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;

try {
    // Do stuff
    ...

} catch (SQLException ex) {
    // Exception handling stuff
    ...
} finally {
    if (rs != null) {
        try {
            rs.close();
        } catch (SQLException e) { /* ignored */}
    }
    if (ps != null) {
        try {
            ps.close();
        } catch (SQLException e) { /* ignored */}
    }
    if (conn != null) {
        try {
            conn.close();
        } catch (SQLException e) { /* ignored */}
    }
}

El finallybloque se puede mejorar ligeramente (para evitar la comprobación nula):

} finally {
    try { rs.close(); } catch (Exception e) { /* ignored */ }
    try { ps.close(); } catch (Exception e) { /* ignored */ }
    try { conn.close(); } catch (Exception e) { /* ignored */ }
}

Pero, aún así, esto es extremadamente detallado, por lo que generalmente terminas usando una clase auxiliar para cerrar los objetos en métodos auxiliares nulos y el finallybloque se convierte en algo así:

} finally {
    DbUtils.closeQuietly(rs);
    DbUtils.closeQuietly(ps);
    DbUtils.closeQuietly(conn);
}

Y, en realidad, Apache Commons DbUtils tiene una DbUtilsclase que está haciendo precisamente eso, por lo que no hay necesidad de escribir la suya.

Pascal Thivent
fuente
3
¡Ayuda increíble, gracias! No entendí ni pensé en las conn! = Declaraciones nulas.
onaclov2000
1
@ onaclov2000 Sí, rs, ps, connpuede ser nulldependiendo de donde se rompe el código. Es por eso que esto se conoce como el patrón "seguro".
Pascal Thivent
12
@Pascal Thivent: en realidad no necesitamos cerrarlos todos. El libro "Core Java Volume two - Advanced Features" ha escrito: El closemétodo de un Statementobjeto cierra automáticamente el asociado ResultSetsi la declaración tiene un conjunto de resultados abierto. Del mismo modo, el closemétodo de la Connectionclase cierra todos Statementslos Connection.
Majid Azimi
12
@Majid: a menos que sea una conexión agrupada. Las declaraciones luego se filtrarían.
BalusC
1
@BalusC: ¿Puede explicar qué sucede cuando se cierra una conexión agrupada utilizando el método connection.close ()
Krsna Chaitanya
61

Siempre es mejor cerrar los objetos de base de datos / recursos después de su uso. Es mejor cerrar la conexión, el conjunto de resultados y los objetos de declaración en el finallybloque.

Hasta Java7, todos estos recursos deben cerrarse utilizando un finallybloque. Si está utilizando Java 7, entonces para cerrar los recursos puede hacer lo siguiente.

try(Connection con = getConnection(url, username, password, "org.postgresql.Driver");
    Statement stmt = con.createStatement();
    ResultSet rs = stmt.executeQuery(sql);
) {

//statements
}catch(....){}

Ahora, los objetos con, stmt y rs se convierten en parte del bloque try y java cierra automáticamente estos recursos después de su uso.

Espero haber sido útil.

Yadu Krishnan
fuente
¿Qué pasa si mi declaración es implícita, es decir, ResultSet rs = conn.createStatement().executeQuery(sql);dentro del trybloque?
Antares42
1
No podrá hacer referencia a ellos en el bloque finalmente {} para el cierre. Si se produce una excepción, el método close () del ResultSet nunca se invocará
Dan
¿Qué pasa si no los cierro?
Alex78191
Si no los cierra, pueden producirse pérdidas de memoria.
Yadu Krishnan
14

Es suficiente para cerrar justo Statementy Connection. No hay necesidad de cerrar explícitamente el ResultSetobjeto.

La documentación de Java dice acerca de java.sql.ResultSet:

Un objeto ResultSet se cierra automáticamente por el objeto de declaración que lo generó cuando ese objeto de declaración se cierra, se vuelve a ejecutar o se utiliza para recuperar el siguiente resultado de una secuencia de resultados múltiples.


Gracias BalusC por sus comentarios: "No confiaría en eso. Algunos controladores JDBC fallan en eso".

Grigori A.
fuente
25
No confiaría en eso. Algunos controladores JDBC fallan en eso. Por ejemplo, Oracle con "Máximo de cursores abiertos excedidos", etc. Simplemente cierre explícitamente todos los recursos abiertos, sin excusas.
BalusC
1
Prefiero no usar controladores que no se ajusten a las especificaciones entonces
Enerccio
2
Como señala BalusC, es una buena programación defensiva cerrar explícitamente la conexión en lugar de conectar una dependencia de un proveedor en particular.
michaelok
11

Si. Debe cerrar el conjunto de resultados, la declaración y la conexión. Si la conexión proviene de un grupo, cerrarla en realidad la envía de vuelta al grupo para su reutilización.

Por lo general, debe hacer esto en un finally{}bloque, de modo que si se produce una excepción, aún tiene la oportunidad de cerrar esto.

Muchos marcos se ocuparán de este problema de asignación de recursos / asignación de recursos por usted. por ejemplo Spring's JdbcTemplate . Apache DbUtils tiene métodos para buscar después de cerrar el conjunto de resultados / declaración / conexión, ya sea nulo o no (y detectar excepciones al cerrar), lo que también puede ayudar.

Brian Agnew
fuente
1
Cuando inserto un eclipse "finalmente" le gusta resaltarlo diciéndome que está mal. ¿Debería esto ir después de los bloques de captura?
onaclov2000
Si. intente {} atrapar {} finalmente {}. La captura {} es opcional, por cierto. Al igual que finalmente {}
Brian Agnew
Moví las declaraciones de "cierre" al final, pero solo dicen "sqlexception", ¿alguna sugerencia?
onaclov2000
1
close () produce una excepción SQLException. Tienes que manejar eso. Ver DbUtils.closeQuietly () para manejar esto en silencio.
Brian Agnew
> ¿Qué sucede realmente si no se produce conn.close ()?
Alex78191
8

En realidad, es mejor si usa un bloque de prueba con recursos y Java cerrará todas las conexiones por usted cuando salga del bloque de prueba.

Debe hacer esto con cualquier objeto que implemente AutoClosable.

try (Connection connection = getDatabaseConnection(); Statement statement = connection.createStatement()) {
    String sqlToExecute = "SELECT * FROM persons";
    try (ResultSet resultSet = statement.execute(sqlToExecute)) {
        if (resultSet.next()) {
            System.out.println(resultSet.getString("name");
        }
    }
} catch (SQLException e) {
    System.out.println("Failed to select persons.");
}

La llamada a getDatabaseConnection está hecha. Reemplácelo con una llamada que le proporcione una conexión JDBC SQL o una conexión de un grupo.

Joe
fuente
¿Entonces no tiene que cerrar manualmente la conexión en este caso?
Colin D
1
Correcto. No tiene que cerrar explícitamente la conexión. Se cerrará cuando se llegue al final del bloque de código de prueba.
Joe
7

Sí, necesitas cerrar la conexión. De lo contrario, el cliente de la base de datos generalmente mantendrá la conexión de socket y otros recursos abiertos.

Alex Miller
fuente
... hasta que salga. Esto vincula varios recursos finitos en el lado del cliente y del servidor. Si un cliente hace demasiado este tipo de cosas, puede causar problemas para el propio cliente, el servicio de base de datos y posiblemente incluso para otras aplicaciones que se ejecutan en la máquina del cliente o servidor.
Stephen C