Fechando conexões de banco de dados em Java

121

Estou ficando um pouco confuso, eu estava lendo o abaixo em 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();
}

Você não precisa fechar a conexão conn? O que realmente está acontecendo se o conn.close () não ocorrer?

Eu tenho um aplicativo Web privado que estou mantendo que atualmente não fecha nenhum dos formulários, mas o importante é realmente o stmt, o conn ou ambos?

O site continua inoperante de forma intermitente, mas o servidor continua dizendo que é um problema de conexão com o banco de dados. Suspeito que não esteja sendo fechado, mas não sei qual fechar.

onaclov2000
fonte
É sempre uma prática recomendada fechar as conexões por conta própria, sem depender de outros drivers e modelos para lidar com o fechamento. A falha no fechamento da conexão resultará em soquetes e recursos abertos para sempre até uma falha (não há mais cenário de recursos) ou reinicialização.
Arun Joshla

Respostas:

196

Quando você terminar de usar o seu Connection, precisará fechá-lo explicitamente chamando seu close()método para liberar quaisquer outros recursos do banco de dados (cursores, identificadores, etc.) nos quais a conexão esteja mantendo.

Na verdade, o padrão seguro em Java é encerrar a sua ResultSet, Statemente Connection(nessa ordem) em um finallybloco quando você está feito com eles, algo assim:

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 */}
    }
}

O finallybloco pode ser ligeiramente aprimorado para (para evitar a verificação nula):

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

Mas, ainda assim, isso é extremamente detalhado, geralmente você acaba usando uma classe auxiliar para fechar os objetos nos métodos auxiliares com segurança nula e o finallybloco se torna algo assim:

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

E, na verdade, o Apache Commons DbUtils tem uma DbUtilsclasse que está fazendo exatamente isso, portanto não há necessidade de escrever sua própria.

Pascal Thivent
fonte
3
Ajuda incrível, obrigado! Eu não entendi ou pensei nas instruções conn! = Null.
Onaclov2000
1
@ onaclov2000 Sim, rs, ps, connpode ser null, dependendo de onde as quebras de código. É por isso que isso é conhecido como padrão "seguro".
Pascal Thivent
12
@ Pascal Thivent: Na verdade, não precisamos fechar todos eles. O livro "Núcleo Java Volume Dois - Recursos Avançados" foi escrito: O closemétodo de um Statementobjeto fecha automaticamente o associado ResultSetse a instrução tiver um conjunto de resultados aberto. Da mesma forma, o closemétodo da Connectionclasse fecha todos Statementsos Connection.
Majid Azimi
12
@ Mahj: A menos que seja uma conexão em pool. As declarações então vazariam.
BalusC
1
@BalusC: u Pode me explicar o que acontece quando uma conexão em pool é fechado usando Connection.Close () Método
Krsna Chaitanya
61

É sempre melhor fechar os objetos de banco de dados / recurso após o uso. Melhor fechar objetos de conexão, conjunto de resultados e instrução no finallybloco.

Até o Java7, todos esses recursos precisam ser fechados usando um finallybloco. Se você estiver usando o Java 7, para fechar os recursos, você pode fazer o seguinte.

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

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

Agora, os objetos con, stmt e rs se tornam parte do bloco try e o java fecha automaticamente esses recursos após o uso.

Espero ter sido útil.

Yadu Krishnan
fonte
E se minha declaração estiver implícita, ou seja, ResultSet rs = conn.createStatement().executeQuery(sql);dentro do trybloco?
Antares42, 12/12/2015
1
Você não poderá referenciá-los no bloco finalmente {} para fechamento. Se uma exceção for lançada, o método close () do ResultSet nunca será chamado
Dan
O que acontece se eu não os fechar?
precisa saber é o seguinte
se você não fechá-los, poderão ocorrer vazamentos de memória.
Yadu Krishnan
14

É suficiente fechar apenas Statemente Connection. Não há necessidade de fechar explicitamente o ResultSetobjeto.

A documentação Java diz sobre java.sql.ResultSet:

Um objeto ResultSet é automaticamente fechado pelo objeto Statement que o gerou quando o objeto Statement é fechado, reexecutado ou é usado para recuperar o próximo resultado de uma sequência de vários resultados.


Obrigado BalusC pelos comentários: "Eu não confiaria nisso. Alguns drivers JDBC falham nisso."

Grigori A.
fonte
25
Eu não confiaria nisso. Alguns drivers JDBC falham nisso. Por exemplo, Oracle com "Máximo de cursores abertos excedidos", etc. Apenas feche explicitamente todos os recursos abertos, sem desculpas.
BalusC
1
Eu preferiria não usar drivers que não estão em conformidade com as especificações então
Enerccio
2
Como o BalusC aponta, é uma boa programação defensiva fechar explicitamente a conexão em vez de conectar uma dependência a um provedor específico.
michaelok
11

Sim. Você precisa fechar o conjunto de resultados, a instrução e a conexão. Se a conexão veio de um pool, o fechamento realmente o envia de volta ao pool para reutilização.

Você normalmente precisa fazer isso em um finally{}bloco, de modo que, se uma exceção for lançada, você ainda terá a chance de fechá-la.

Muitas estruturas cuidam desse problema de alocação / desalocação de recursos para você. por exemplo, o JdbcTemplate do Spring . O Apache DbUtils possui métodos para cuidar do fechamento do conjunto de resultados / instrução / conexão, nulo ou não (e captura de exceções ao fechar), o que também pode ajudar.

Brian Agnew
fonte
1
Quando insiro um eclipse "finalmente", gosto de destacá-lo, dizendo que está errado. isso deve ir após os blocos de captura?
Onaclov2000
Sim. tente {} pegar {} finalmente {}. A captura {} é opcional, btw. Assim como o finalmente {}
Brian Agnew
Eu mudei as instruções "close" para finalmente, mas elas estão dizendo "sqlexception", alguma sugestão?
Onaclov2000
1
close () lança uma SQLException. Você tem que lidar com isso. Consulte DbUtils.closeQuietly () para lidar com isso silenciosamente.
Brian Agnew
> O que realmente está acontecendo se o conn.close () não ocorrer?
precisa saber é o seguinte
8

Na verdade, é melhor se você usar um bloco try-with-resources e o Java fechará todas as conexões quando você sair do bloco try.

Você deve fazer isso com qualquer objeto que implemente o 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.");
}

A chamada para getDatabaseConnection é apenas composta. Substitua-o por uma chamada que obtenha uma conexão JDBC SQL ou uma conexão de um pool.

Joe
fonte
Então você não precisa fechar manualmente a conexão neste caso?
Colin D
1
Corrigir. Você não precisa fechar explicitamente a conexão. Será fechado quando o final do bloco de código de tentativa for alcançado.
Joe
7

Sim, você precisa fechar a conexão. Caso contrário, o cliente de banco de dados normalmente manterá a conexão do soquete e outros recursos abertos.

Alex Miller
fonte
... até sair. Isso vincula vários recursos finitos no lado do cliente e servidor. Se um cliente faz esse tipo de coisa demais, pode causar problemas para o próprio cliente, o serviço de banco de dados e, possivelmente, até para outros aplicativos em execução na máquina cliente ou servidor.
Stephen C