Pra quê um monte de DAOs cheios de métodos?

Opa galera,

Eu tava escrevendo umas coisas aqui pra colocar num material sobre o Hibernate e uma das partes seria mostrar como implementar um DAO usando o Hibernate. Me baseando numa implementação de um sistema que eu estou desenvolvendo aqui na universidade, terminei esbarrando em uma das diretivas do padrão, a definição de um método para cada query feita no banco.

Sendo uma implementação feita com o Hibernate, eu pensei em não seguir o padrão nessa onda de “um método pra cada query”. Criei um DAO genérico, que insere, atualiza, deleta, lista e busca usando “exemplos” e dentro desse mesmo DAO coloquei mais um método, executarBusca a assinatura ficou assim:

O parâmetro String é apenas o nome da Named Query que foi definida lá nos arquivos de mapeamento e o Map é o conjunto de pares “nome-parâmetro” da query. Por exemplo, a query pode ser:

Então eu colocaria um objeto no Map assim:

O próprio método se encarregaria de colocar o parâmetro lá.

Fazendo desse modo, a quantidade de código a ser mantido diminui de uma maneira drástica, porque eu não vou mais precisar escrever um método pra cada select, a maioria deles vai estar definida como Named Queries nos arquivos de mapeamento do Hibernate.

Eu pensei em criar métodos de busca nos DAOs quando fosse necessário usar código específico de um banco ou quando as pesquisas fossem feitas usando a Criteria Query API.

Alguém tem alguma idéia sobre alguma outra maneira que isso poderia ser feito? Alguém já fez coisa parecida?

[editado]Editei no nome pra ficar mais relacionado ao assunto né[/editado]

O problema é que você está forçando a classe que está usando o DAO a saber detalhes da persistência (nesse caso, saber o nome da NamedQuery), quebrando o conceito de DAO. E isso é feio, tão feio quanto passar um SQL pra um DAO non-hibernate. Só pq é uma namedQuery, e vai usar hibernate, não torna mais bonito. :smiley:

Com o sono que eu tou, a única sugestão que eu posso dá é você tornar o executarBusca() como private, criar métodos que recebam um mapa de parametros, chame o executarBusca() passando a namedQuery preterida e os parametros.

[code]private List executarBusca(String namedQuery, Map parametros) {

}

public List buscarAlgumaCoisa(Map parametros) {
return executarBusca(“namedQueryParaBuscarAlgumasCoisas”, parametros);
}[/code]

Mas assim ele ainda vai ter que saber o nome do método.

E se eu for inserir paginação? Vou ter que fazer o overload de todos os métodos passando também os parâmetros da paginação?

Eita padrão complicado danado… :shock:

Não entendi. É um problema a classe que está usando o DAO saber o nome de um método desse DAO?

É o preço a ser pago para se ter um método super-fantástico-faz-tudo-da-estrela. Mas… pq não?

Saber o nome do parâmetro não seria praticamente a mesma coisa de saber o nome do método não?

Um método pra cada select é tanto código pra se manter… :shock:

No exemplo simplista que eu dei, seria “praticamente” a mesma coisa.

Invés de um hiper-método que já fizesse a consulta, você poderia ter um que retornasse um objeto Query, daí quando precisasse de mais algum tratamento na consulta (como paginação) faria no método.

public List buscarAlgumaCoisa(Map parametros, int inicio, int quantidade) { Query query = superMetodoQueCriaObjetoQuery("namedQuery", parametros); query.setFirstResult(inicio); query.setMaxResults(quantidade); return query.list(); }

Exemplo que pode ser usado pra responder a primeira pergunta! :wink:

Relendo o tópico inicial, vi que a sugestão que eu dei não soluciona o seu problema, que é ter muitos métodos no DAO. :roll:

Mas o problema é ter muitos métodos ou é ter muito código dentros desses métodos? Você quer abstrair todo o trabalho de passagem de parametros para a consulta, deixando os métodos mais limpos, ou simplismente limpar a interface da classe, deixando com poucos métodos?

O problema é ter muitos métodos que fazem exatamente a mesma coisa. Praticamente todos vão ter as mesmas chamadas, sendo passados do mesmo jeito e ainda vai ter o problema da paginação, onde todos os métodos vão ser sobrecarregados pra ter o contador do primeiro registro e a quantidade de registros que ele deve receber.

No padrão DAO, agente coloca vários métodos pra conseguir independência do banco ou de como as informações vão ser serializadas, só que usando o Hibernate, eu já tenho meio caminho andado, não vou mais me preocupar tanto com independência de bancos de dados.

Será que é realmente necessário seguir o padrão, se a proposta dele já está sendo cumprida?

Maurício,

Entendi sua sugestão, mas tenho problemas.

Vamos abstrair mais. Imagina que eu possua umas constantes:

BUSQUE_POR_NOME
BUSQUE_POR_IDADE
BUSQUE_POR_SEXO

E que estas sejam utilizadas para achar as queries no seu arquivo (dá pra fazer em SQL também, colocando queries em properties).

E um método:

public List buscar(int query, Map parametros)

Você tem um método para sua classe conhecer, mais três constantes. Isso é tão diferente de ter vários métodos assim?

A classe que for chamar o DAO aidna vai precisar saber qual tipo de busca será feita.

Você teria vários métodos, mas a lógica de montar um objeto baseado em registro, paginar, etc. pode ser reaproveitada por todos.

Não defendo um nem outro (acho os dois ruins, na verdade :P) mas qual o real benefício?

Shoes

Sei lá, a minha luta é pra não definir um milhão de DAOs e métodos que vão fazer a mesma coisa.

Mas como você acha que isso poderia ser feito Phillip?

Não sei, por isso uso DAOs :frowning:

Ta aí, mas de todo jeito esse uso de “identificadores” como você colocou no método é uma boa, fica melhor do que simplesmente passar o nome da query ou criar algumas dúzias de métodos pra fazer praticamente a mesma coisa. Vou ver o que eu consigo fazer aqui com enums pra botar isso na prática.

Cara, essa dúzia de métodos farão a mesma coisa por causa da implementação de acesso a dados que está utilizando.

E se fosse criar um DAO que busca numa coleção Java? Tenho certeza que essa dúzia de métodos fariam coisas bem diferentes ao invés de simplesmente passar uma query. “Ah, mas só vou usar jdbc mesmo.” Ok então :wink:

[quote=LIPE]Cara, essa dúzia de métodos farão a mesma coisa por causa da implementação de acesso a dados que está utilizando.

E se fosse criar um DAO que busca numa coleção Java? Tenho certeza que essa dúzia de métodos fariam coisas bem diferentes ao invés de simplesmente passar uma query. “Ah, mas só vou usar jdbc mesmo.” Ok então ;)[/quote]

Pois é LIPE, no início eu tava até pensando sobre isso, “E se eu mudar pra um banco OO” ou “E se eu mudar pra um banco em XML”, mas eu to trabalhando com o Db4o (um banco OO pra Java e .Net) num trabalho aqui da faculdade (usando C#) e espero não ter que mexer com isso denovo nem tão cedo, é muito complicado de se trabalhar!

Acho que os bancos relacionais vão continuar por aí ainda por um boooom tempo :mrgreen:

Claro que sim. E também há várias maneiras e frameworks para acessar um banco de dados relacional.

Mas o problema não é esse, concordo com você em se ater ao necessário por hora. O lance do cliente precisar entender da implementação que acho problemático. Se você mudar de idéia e não querer mais o usar as queries, terá que alterar TODOS os clientes das classes, e não só o objeto que acessa os dados. E isso é algo a se temer.

Vi em um lugar uma dica interessante sobre criar Objetos de seleção:


public class FuncionarioSelecao {
   public void setRegistroIniciall(int registroInicial) {}
   public void filtrarPorCodigo(int codigo) {}
   public void filtrarPorNome(String nome) {}
   public void filtrarPorEndereco(String endereco) {}
}

public class FuncionarioDAO {
   public void salvar(Funcionario funcionario) {}
   public List consultar(FuncionarioSelecao selecao) {}
}

FuncionarioSelecao fs = new FuncionarioSelecao();
fs.filtrarPorNome("Maria");
Lista listaFuncionarios  = FuncionarioDAO.consultar(fs);

[quote=jprogrammer]Vi em um lugar uma dica interessante sobre criar Objetos de seleção:

[code]

public class FuncionarioSelecao {
public void setRegistroIniciall(int registroInicial) {}
public void filtrarPorCodigo(int codigo) {}
public void filtrarPorNome(String nome) {}
public void filtrarPorEndereco(String endereco) {}
}[/code][/quote]
Essa classe FuncionarioSelecao parece apenas transferir o local onde os vários metodos para consulta estão. Quando houver um número muito grande de parametros buscáveis, vai haver uma série de filtrar*? Se for assim, qual a real vantagem disso sobre criar métodos especificos?

valeuz…

Na verdade essa classe irá conter os métodos associados com os atributos “buscáveis” da classe, ou seja, os parametros que podem ser usados em várias consultas diferentes. Isso traz uma maior reaproveitamento.
ex:


FuncionarioSelecao fs = new FuncionarioSelecao();
fs.filtrarPorCidade("São Paulo");
fs.filtrarPorIdadeMaiorQue(12);
fs.setTotalRegistros(10);
fs.setRegistraoInicial(10);

FuncionarioDAO.consultarFuncionariosEmDestaque(fs,Mes.JANEIRO);

Esse método irá trazer os funcionarios em destaque do mês de Janeiro (regra especifica do método) da cidade de são paulo e idade maior que 12.
Isso com paginação de 10 registros a partir do 10 registro.

É exatamente esse tipo de coisa que eu quero evitar, milhares de classes e métodos pra fazer o mesmo serviço…

Se o framework de persistência dá essa possibilidade, porque não usar? Regra do mínimo de funcionalidade comum? Alguém aí falou de AWT?

Sei lá, as vezes é tanta “independência” disso ou daquilo que agente acaba sem “liberdade”.

Outra alternativa para diminuir a quantidade de métodos do Dao é deixar apenas métodos simples CRUD e findAll() , por exemplo, e buscas por Nome, endereço ou coisas do tipo se tornarem regras de negócio (processamento feito sobre os objetos) .
Óbvio que isso se torna enviável se sua base de dados é imensa, ai eu acho que não tem muito como escapar, por questão de performance o mais fácil eh implementar várias consultas diferentes.