Quando comecei a programar na escola (curso técnico que fiz na época), comecei em uma das linguagens mais legais e, de certo modo, estranha que existe, a linguagem C. Alguns conceitos dela não são claros logo de inicio, pois não fazem nenhum paralelo com o mundo real. Um deles é o conceito de ponteiros e alocação de memória, onde você dinamicamente pode alocar memória para seu programa utilizar. Porém, como dizia o Tio Ben: “Grandes poderes trazem grandes responsabilidades”, logo você se via obrigado a “lembrar” de liberar toda a memória alocada quando não fizesse mais uso dela, senão seu programa logo travaria junto com o seu sistema operacional.
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:
| |
O nosso código principal, que faz uso da classe CarroDao:
| |
Até este momento, não teremos problemas, porém podemos agora tentar utilizar novamente o carroDaoe realizar alguma outra operação:
| |
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:
| |
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:
| |
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:
| |
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.