O mundo mudou, os tempos agora são outros, temos a internet, o relógio digital e o Garbage Collector, que faz toda a gerência da coleta de memória que não é mais utilizada por nossos objetos. Porém agora temos que gerênciar outros recursos, como objetos que abrem arquivos e devem fecha-los ou conexões com o banco de dados. Caso você não os feche, seu arquivo não poderá ser aberto novamente ou seu banco de dados começará a recusar conexões, e sinceramente, isso é muito chato.
Por isso, vamos lembrar de fechar todas as nossas conexões nos nossos DAOs:
public class CarroDao { private Connection connection; private String INSERIR_CARRO = "INSERT INTO carros(nome, marca) VALUES(?, ? )"; private String EXCLUIR_CARRO = "DELETE FROM carros WHERE id = ?"; private String RECUPERAR_CARRO = "SELECT id, nome, marca FROM carros WHERE id = ?"; public CarroDao() { connection = ConnectionFactory.getConnection(); } public void inserir(Carro carro) {...} public void excluir(int id) {...} public void recuperar(int id) {...} private void fecharRecursos(ResultSet rs, PreparedStatement ps) {...} public void fechaConexao() { try { connection.close(); } catch(SQLException e) { System.err.println("Problemas ao fechar a conexao: " + e.getMessage()); } } }O nosso código principal, que faz uso da classe CarroDao:
public class AppMain { public static void main(String[] args) { Carro carro = new Carro(); carro.setNome("Time Machine"); carro.setMarca("DeLorean"); CarroDao carroDao = new CarroDao(); carroDao.inserir(carro); carroDao.fechaConexao(); } }Até este momento, não teremos problemas, porém podemos agora tentar utilizar novamente o carroDao e realizar alguma outra operação:
public class AppMain { public static void main(String[] args) { //...Codigo anterior //Outra operação carroDao.excluir(1); carroDao.fechaConexao(); } }E funciona normalmente, mas se mantivermos esse padrão e criarmos outro Dao, por exemplo, PessoaDao, que também instanciará uma Connection para ela e também possuirá o método fecharConexao, que também devemos lembrar de fechar:
public class AppMain { public static void main(String[] args) { //...Codigo anterior //Outra operação carroDao.excluir(1); carroDao.fechaConexao(); Pessoa pessoa = new Pessoa("Marty McFly"); PessoaDao pessoaDao = new PessoaDao(); pessoaDao.inserir(pessoa); pessoaDao.fecharConexao(); } }Perceba que até o momento criamos dois DAOs e também duas conexões com o banco de dados, cada DAO contendo a sua conexão, porém como é de boa prática, não devemos criar conexões indiscriminadamente com o banco, sendo a melhor solucão usar um pool de conexões e também caso as operações estejam em um mesmo contexto, utilizar a mesma conexão para realizar todas as operações e no final, fechar ela.
Porém como podemos fazer isso de maneira limpa e simples? Poderiamos dar um getConnection do CarroDao e recuperar a conexão dele e usar para PessoaDao também, mas e se usarmos o PessoaDao sem querermos utilizar o CarroDao, o que faremos? O termo DAO vem de Data Access Object, sendo utilizado para realizar a recuperação dos dados e a conversão em objetos, deixando claro que não é de sua responsabilidade criar conexões e acessar os recursos.
De maneira mais limpa, podemos simplesmente alterar os nossos DAOs para receberem a conexão dos seus clientes, deixando a cargo deles a criação e manutenção da conexão com o banco. Porém os DAOs precisam obrigatoriamente receber a conexão, por isso vamos colocar Connection como parâmetro do construtor dos DAOs e podemos remover os métodos fecharConexao:
public class CarroDao { private Connection connection; public CarroDao(Connection connection) { this.connection = connection; } //...metodos de manipução já existentes, e removido o método fecharConexao }Nossa classe CarroDao agora está bem mais coerente com o seu papel, agora devemos criar a conexão dentro do código cliente, utilizando ela onde for necessário:
public class AppMain { public static void main(String[] args) { Carro carro = new Carro(); carro.setNome("Time Machine"); carro.setMarca("DeLorean"); Connection connection = ConnectionFactory.getConnection(); CarroDao carroDao = new CarroDao(connection); carroDao.inserir(carro); carroDao.excluir(1); Pessoa pessoa = new Pessoa("Marty McFly"); PessoaDao pessoaDao = new PessoaDao(connection); pessoaDao.inserir(pessoa); try { connection.close(); } catch(SQLException e) { System.err.println("Problemas ao fechar a conexao: " + e.getMessage()); } } }Com apenas uma conexão conseguimos realizar todas as nossas operações, e facilmente também podemos transacionar a nossa operação, caso ocorra algum problema, realizamos o rollback de todas as operações anteriores que utilizaram a mesma conexão e devido a isto estão no mesmo contexto.
O nome deste padrão de desenvolvimento é Inversão de Controle, onde você repassa o gerenciamento de recursos um nível acima e recuperando os mesmos onde for necessário através de injeção de dependência (que no nosso caso, obrigamos a passar pelo construtor).
A inversão de controle e a injeção de dependências (em inglês, Inversion of Control - IoC, e Dependency Injection - DI, respectivamente), além de permitirem que tenhamos classes com menos responsabilidades e o mais interessante, que possamos gerenciar facilmente objetos que acessam recursos externos. No nosso caso, a conexão estava sendo gerenciada direto pela aplicação exemplo (AppMain), porém podemos facilmente deixar a cargo de algum framework ou container de aplicação este gerenciamento, e nuances como o local do banco de dados, usuários e senhas, serem facilmente configuradas por XMLs ou annotations.
No futuro teremos um artigo mostrando como usar a injeção de depedências com o framework mais conhecido para esta tarefa, o Spring, mostrando sua configuração básica e modo de operação.
Nenhum comentário:
Postar um comentário