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