Fechando conexões JDBC no pool

109

Nossa seção de código padrão para usar JDBC é ...

Connection conn = getConnection(...);
Statement  stmt = conn.conn.createStatement (ResultSet.TYPE_SCROLL_INSENSITIVE,
                                                ResultSet.CONCUR_READ_ONLY);
ResultSet  rset = stmt.executeQuery (sqlQuery);

// do stuff with rset

rset.close(); stmt.close(); conn.close();

Pergunta 1: Ao usar o pool de conexões, deve-se fechar a conexão no final? Em caso afirmativo, o objetivo do pool não está perdido? E se não, como o DataSource sabe quando uma determinada instância do Connection é liberada e pode ser reutilizada? Estou um pouco confuso com este, quaisquer dicas serão apreciadas.

Questão 2: o método a seguir está próximo do padrão? Parece uma tentativa de obter uma conexão do pool e, se o DataSource não puder ser estabelecido, use o antigo DriverManager. Não temos certeza de qual parte está sendo executada em tempo de execução. Repetindo a pergunta acima, deve-se fechar a Conexão que sai de tal método?

Obrigado, - MS.

synchronized public Connection getConnection (boolean pooledConnection)
                                                        throws SQLException {
        if (pooledConnection) {
                if (ds == null) {
                        try {
                                Context envCtx = (Context)
                                        new InitialContext().lookup("java:comp/env");
                                ds = (DataSource) envCtx.lookup("jdbc/NamedInTomcat");
                                return ds.getConnection();
                        } catch (NamingException e) {
                                e.printStackTrace();
                }}
                return (ds == null) ? getConnection (false) : ds.getConnection();
        }
        return DriverManager.getConnection(
                "jdbc:mysql://"+ipaddy+":"+dbPort +"/" + dbName, uName, pWord);
}

Edit: Acho que estamos obtendo a conexão em pool, pois não vemos um rastreamento de pilha.

Manidip Sengupta
fonte

Respostas:

121

Ao usar o pool de conexão, deve-se fechar a conexão no final? Em caso afirmativo, o objetivo do pool não está perdido? E se não, como o DataSource sabe quando uma determinada instância do Connection é liberada e pode ser reutilizada? Estou um pouco confuso com este, quaisquer dicas serão apreciadas.

Sim, certamente você também precisa fechar a conexão em pool. Na verdade, é um invólucro em torno da conexão real. Por baixo das tampas, libertará a ligação real de volta à piscina. Cabe ao pool decidir se a conexão real será realmente encerrada ou reutilizada para uma nova getConnection()chamada. Portanto, independentemente de estar usando um pool de conexão ou não, você deve sempre fechar todos os recursos JDBC na ordem reversa no finallybloco do trybloco onde você os adquiriu. Em Java 7, isso pode ser ainda mais simplificado usando a try-with-resourcesinstrução.


O método a seguir está próximo do padrão? Parece uma tentativa de obter uma conexão do pool, e se o DataSource não puder ser estabelecido, use o antigo DriverManager. Não temos certeza de qual parte está sendo executada em tempo de execução. Repetindo a pergunta acima, deve-se fechar a Conexão que sai de tal método?

O exemplo é bastante assustador. Você só precisa pesquisar / inicializar a DataSourceúnica vez durante a inicialização do aplicativo em algum construtor / inicialização de uma classe de configuração de banco de dados em todo o aplicativo. Em seguida, basta chamar getConnection()a única e mesma fonte de dados durante o resto da vida útil do aplicativo. Não há necessidade de sincronização nem verificação de nulos.

Veja também:

BalusC
fonte
Isso é o que está fazendo (inicializar uma vez), não é? ds é uma variável de instância e if (ds == null) ... é a parte de inicialização.
Manidip Sengupta
Fazer as verificações todas as vezes em um método get getConnection()é estranho. Basta fazer em c'tor ou bloco de inicialização da mesma classe, sem sincronização / verificação de nulos. Será chamado apenas uma vez. Para mais dicas e exemplos iniciais, você pode achar este artigo útil.
BalusC de
Excelente artigo, BalusC. A classe com a qual estou lidando praticamente implementa a camada de dados, usando DTOs. Eu concordo com você, a inicialização deve ser no construtor. Agora, esta classe tem uma tonelada de métodos, cada um com conn, stmt e rset como variáveis ​​locais, as conexões estão em um bloco try e, finalmente, há uma chamada de 1 linha csrClose (conn, stmt, rset), onde todos os 3 estão fechados (na ordem inversa). Agora, o DTO que você desenvolveu no exemplo é uma imagem espelhada de uma linha de tabela de banco de dados. Temos consultas SQL complexas com junções (e outras cláusulas). Você tem um artigo sobre como desenvolver DAO para esses resultados?
Manidip Sengupta
2
@yat: Você DEVE chamar close()todos eles no finallybloco do mesmo trybloco onde você os adquiriu / criou. Isso é completamente independente de ser uma conexão em pool ou não.
BalusC
1
@iJava: essa piscina foi escrita por um amador que não tem ideia do que está fazendo. Ignore isso e vá para uma biblioteca de verdade. Por exemplo, HikariCP.
BalusC de
22

Os pools normalmente retornam a você um objeto Connection encapsulado, onde o método close () é substituído, normalmente retornando a Connection ao pool. Chamar close () está OK e provavelmente ainda é necessário.

Um método close () provavelmente seria assim:

public void close() throws SQLException {
  pool.returnConnection(this);
}

Para sua segunda pergunta, você pode adicionar um logger para mostrar se o bloco inferior funciona. Eu imagino que você só queira de uma forma ou de outra para a configuração de suas conexões de banco de dados. Usamos apenas um pool para nossos acessos ao banco de dados. De qualquer forma, fechar a conexão seria muito importante para evitar vazamentos.

taer
fonte
Concordo, temos um logger e ele também pode ser usado aqui. Preciso estudar um pouco como você pode encapsular um objeto, substituir seu método close (), mas ainda manter o mesmo nome de classe (conexão)
Manidip Sengupta
1
Calling close() is OK and probably still required., não chamar close irá vazar a conexão, a menos que o pool implemente alguma estratégia de recuperação
svarog
0

Na verdade, a melhor abordagem para gerenciamento de conexão é não transferi-los para nenhum código em qualquer lugar.

Crie uma classe SQLExecutor que é o único local que abre e fecha conexões.

O restante do aplicativo, então, bombeia instruções para o executor, em vez de obter conexões do pool e gerenciá-las (ou gerenciá-las incorretamente) em todo o lugar.

Você pode ter quantas instâncias do executor desejar, mas ninguém deve escrever código que abre e fecha conexões em seu próprio nome.

Convenientemente, isso também permite que você registre todo o seu SQL a partir de um único conjunto de código.

Rodney P. Barbati
fonte