Vamos discutir sobre DAOs genericos!

Pessoal andei pesquisando por ae sobre daos genericos, e não gostei muito do que vi, ou nao entendi muito bem.

Bom o exemplo em questão é este que colo aqui:
Feito pelo ralphsilver neste tópico: http://www.guj.com.br/posts/list/140871.java

Minha idéia de dao generico é que ele seja generico e faça tudo pra mim, com qualquer tipo de object que eu envie pra ele.
Então nao poderiamos parar no BaseDaoImpl???

Porque é que eu preciso ir alem e ter mais uma interface e uma classe por ex: PessoaDao e PessoaDaoImpl??

Estou enferrujado e nao estou entendendo este conceito.

E outra imaginem eu com um CRUD de 100 Entities, neste caso vou precisar criar + 100 interfaces Dao e + 100 classes DaoImpl, fala sério 300 arquivos sendo que poderiam ser 100 e o BaseDaoImpl dar conta de fazer qualquer coisa com eles?? Na verdade este BaseDaoImpl ja consegue fazer tudo, o que nao entendo é porque ter mais uma interface e uma classe para cada entity.

Alguem tem uma outra versão de Daos genericos ai pra oferecer??

[code]import java.util.List;

public interface BaseDAO {
/*
* Ações possíveis feita com CRUD. Os métodos cujos parâmetros
* são do tipo java.util.List, são enviados em massa, ou seja,
* os dados são processados e armazenados e apenas no final o
* aplicativo lança o commit. Para grande massa de dados é mais
* viável pois o tempo de processo é muito maior quando abre apenas
* uma conexão e aplica apenas uma ação de persistencia
* */
public T remove(T object) throws Exception;
public T persist(T object) throws Exception;
public T update(T object) throws Exception;

public List<T> update(List<T> object) throws Exception;
public List<T> persist(List<T> list) throws Exception;
public List<T> remove(List<T> object) throws Exception;
/*
 * Aqui se aplica todos os tipos de buscas possível para se fazer
 * dentro de uma aplicação padrão.
 * */
public List<T> findAll() throws Exception;

public T load(Object obj) throws Exception;

}[/code]

classe abstrata DAOImpl:

[code]import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;

import br.edu.unianhanguera.plt.dao.BaseDAO;

public abstract class BaseDAOImpl implements BaseDAO {
protected EntityManager em = null;

@Override
public abstract List<T> findAll() throws Exception;
@Override
public abstract T load(Object obj) throws Exception;

@Override
public T persist(T object) throws Exception {
	EntityTransaction transaction = em.getTransaction();
	transaction.begin();
	try{
		em.persist(object);
		transaction.commit();
		return object;	
	}catch(Exception e){
		transaction.rollback();
		throw e;
	}
}

@Override
public List<T> persist(List<T> list) throws Exception {
	EntityTransaction transaction = em.getTransaction();
	transaction.begin();
	try{
		for(T object:list)
			em.persist(object);
		transaction.commit();
		return list;	
	}catch(Exception e){
		transaction.rollback();
		throw e;
	}
}

@Override
public T remove(T object) throws Exception {
	EntityTransaction transaction = em.getTransaction();
	transaction.begin();
	try{
		em.remove(object);
		transaction.commit();
		return object;	
	}catch(Exception e){
		transaction.rollback();
		throw e;
	}
}

@Override
public List<T> remove(List<T> list) throws Exception {
	EntityTransaction transaction = em.getTransaction();
	transaction.begin();
	try{
		for(T object:list)
			em.remove(object);
		transaction.commit();
		return list;	
	}catch(Exception e){
		transaction.rollback();
		throw e;
	}
}

@Override
public T update(T object) throws Exception {
	EntityTransaction transaction = em.getTransaction();
	transaction.begin();
	try{
		em.merge(object);
		transaction.commit();
		return object;	
	}catch(Exception e){
		transaction.rollback();
		throw e;
	}
}

@Override
public List<T> update(List<T> list) throws Exception {
	EntityTransaction transaction = em.getTransaction();
	transaction.begin();
	try{
		for(T object:list)
			em.merge(object);
		transaction.commit();
		return list;	
	}catch(Exception e){
		transaction.rollback();
		throw e;
	}
}

}[/code]

quando eu preciso criar uma classe (Pessoa por exemplo) eu a interface DAO:

e o DAOImpl

ola Fred. Excelente pergunta! Algumas opinioes minhas:

  • para 300 classes de modelo, voce nao precisa de 300 DAOs: voce pode (e deve) agrupar os que fizerem sentido.
  • nao pegue catch em Exception: o elhor é fazer o tratamento correto de PersistenceException e deixar um finally bem escrito
  • cuidado com as transacoes : é melhor deixar o controla transacional fora, pois ai voce pode agrupar operacoes do dao. caso contrario voce tera problemas para criar uma transacao que insira dois tipos de objetos diferentes, por exemplo
  • eu hoje em dia ja nao gosto de dao generico, ainda mais para usar com heranca: traz muita dor de cabeca, e se voce for ver, sem esse codigo transacional, ele resolve pouca coisa. e a melhor maneira sempre é compor em vez de herdar!

abracos

Quanto ao fato de criar cada uma classe DAO para cada entidade não tem como fugir,mas realmente não faz sentido ter uma interface para cada entidade, então apenas não a implemente.

Nesse caso que vc postou, não bastaria fazer assim:

Não sei se precisa criar uma interface para Pessoa, acho de mais.

[quote=Paulo Silveira]ola Fred. Excelente pergunta! Algumas opinioes minhas:

  • para 300 classes de modelo, voce nao precisa de 300 DAOs: voce pode (e deve) agrupar os que fizerem sentido.
  • nao pegue catch em Exception: o elhor é fazer o tratamento correto de PersistenceException e deixar um finally bem escrito
  • cuidado com as transacoes : é melhor deixar o controla transacional fora, pois ai voce pode agrupar operacoes do dao. caso contrario voce tera problemas para criar uma transacao que insira dois tipos de objetos diferentes, por exemplo
  • eu hoje em dia ja nao gosto de dao generico, ainda mais para usar com heranca: traz muita dor de cabeca, e se voce for ver, sem esse codigo transacional, ele resolve pouca coisa. e a melhor maneira sempre é compor em vez de herdar!

abracos
[/quote]

Opa Paulo blz.

Então, sobre as exceptions e etc no exemplo postado, apenas copiei e colei do outro topico para sevir de base a pergunta, este não é de fato o meu codigo, ainda estou atraz de respostas :smiley: Mas seus conselhos estão anotados.

Aproveitando sua presença no topico, estou justamento começando um projeto com vRaptor, e estou atraz da opção mais elegante e menos trabalhosa para a camada de persistencia, tirando proveito da injeção de dependencia, interceptors e tudo mais que o vRaptor tem a oferecer.

Certo, então sobre o exemplo citado voce acha que eu deveria parar no BaseDaoImpl, e então fazer daos para certos grupos de entities, seria isto?

E ainda sobre compor ai inves de herdar, me corriga se entendi seu ponto errado, eu teria então o BaseDaoImpl concreto, e um outro tipo de dao ± assim:

public class ExemploDao {
   private BaseDaoImpl baseDao;

   public RHDao(BaseDao baseDao)
      this.baseDao = baseDao;
   }
}
...

E ainda por ultimo, voce diz que não gosta de dao generico, qual seria sua abordagem para a persistencia hoje em dia, é disto que estou atras, de uma abordagem mais contemporania, usando o que a OO e o vRaptor tem a nos oferecer.

Grato.

[quote=romarcio]Nesse caso que vc postou, não bastaria fazer assim:

Não sei se precisa criar uma interface para Pessoa, acho de mais. [/quote]

Ao meu ver sim, mas mesmo neste exemplo eu ainda teria 100 classes modelos e outras 100 dao, claro que as operações basicas ja estariam prontas.

Porem é justamente isto que estou tentando entender, em minhas pesquisas no google, a maioria dos exemplos que encontro estão sempre deste jeito, queria saber o porque. Sera que um cara fez uma vez e o resto saiu copiando e postando :shock: :shock: :lol: :lol:

[quote=fredferrao][quote=romarcio]Nesse caso que vc postou, não bastaria fazer assim:

Não sei se precisa criar uma interface para Pessoa, acho de mais. [/quote]

Ao meu ver sim, mas mesmo neste exemplo eu ainda teria 100 classes modelos e outras 100 dao, claro que as operações basicas ja estariam prontas.

Porem é justamente isto que estou tentando entender, em minhas pesquisas no google, a maioria dos exemplos que encontro estão sempre deste jeito, queria saber o porque. Sera que um cara fez uma vez e o resto saiu copiando e postando :shock: :shock: :lol: :lol: [/quote]

Sim, mas disso acho que vc nem deve tentar fugir, pq é a Orientação Objetos.

A classe PessoaEntity, seria apenas a entidade com getters, setters e a classe PessoaDAO teria outra função que seria a realização das consultas e tudo mais envolvendo banco de dados.
O padrão é não mistura-las mesmo.

Criar as interfaces e DAO’s para cada entidade geralmente é feito para se ganhar flexibilidade.

Se você usa o mesmo DAO genérico para todas as entidades, está assumindo que a persistência será igual para todas as entidades, o que nem sempre é verdade.

Em algum momento você pode querer tratar especificidades de cada entidade, e se tudo estiver desacoplado desde o início isso se torna mais fácil, principalmente se você estiver tratando com uma quantidade grande de entidades como você especulou.

Se o problema for escrever tantos arquivos, bom, se você tem o seu ambiente, não custa nada perder alguns minutos criando um pequeno gerador de código pra isso.

[quote=romarcio][quote=fredferrao][quote=romarcio]Nesse caso que vc postou, não bastaria fazer assim:

Não sei se precisa criar uma interface para Pessoa, acho de mais. [/quote]

Ao meu ver sim, mas mesmo neste exemplo eu ainda teria 100 classes modelos e outras 100 dao, claro que as operações basicas ja estariam prontas.

Porem é justamente isto que estou tentando entender, em minhas pesquisas no google, a maioria dos exemplos que encontro estão sempre deste jeito, queria saber o porque. Sera que um cara fez uma vez e o resto saiu copiando e postando :shock: :shock: :lol: :lol: [/quote]

Sim, mas disso acho que vc nem deve tentar fugir, pq é a Orientação Objetos.

A classe PessoaEntity, seria apenas a entidade com getters, setters e a classe PessoaDAO teria outra função que seria a realização das consultas e tudo mais envolvendo banco de dados.
O padrão é não mistura-las mesmo.[/quote]

Com certeza voce esta certo, PessoaEntity é a classe burra com getters e setters e jpa annotation, nao foi isto que quis dizer.

Eu esta pensando em algo como DaoGenericoParaQualquerEntity + PessoaEntity, ClienteEntity, CidadeEntity, etc, quando digo economizar é disto que estou falando, fazer apenas um DAO que sirva a todos.

[quote=juliofsn]Criar as interfaces e DAO’s para cada entidade geralmente é feito para se ganhar flexibilidade.

Se você usa o mesmo DAO genérico para todas as entidades, está assumindo que a persistência será igual para todas as entidades, o que nem sempre é verdade.

Em algum momento você pode querer tratar especificidades de cada entidade, e se tudo estiver desacoplado desde o início isso se torna mais fácil, principalmente se você estiver tratando com uma quantidade grande de entidades como você especulou.

Se o problema for escrever tantos arquivos, bom, se você tem o seu ambiente, não custa nada perder alguns minutos criando um pequeno gerador de código pra isso.[/quote]

Sim, eu pensei nisso. Mas neste caso eu faria apenas se fosse necessario, não? Se um ou outro precisasse de tratamento especial ai sim caberia uma herança e tals.

[quote=fredferrao]Se um ou outro precisasse de tratamento especial ai sim caberia uma herança e tals.
[/quote]

Mas aí, imagine se você tiver que tratar a classe Pessoa, você teria que criar o DAO para Pessoa e na camada acima, mudar a classe que utiliza o DAO genérico para fazer operações com Pessoa, trocando pelo DAO específico que você criou.

Se você já tem um DAO para cada entidade, você só teria que mudar o DAO de Pessoa.

[quote=juliofsn]

Mas aí, imagine se você tiver que tratar a classe Pessoa, você teria que criar o DAO para Pessoa e na camada acima, mudar a classe que utiliza o DAO genérico para fazer operações com Pessoa, trocando pelo DAO específico que você criou.

Se você já tem um DAO para cada entidade, você só teria que mudar o DAO de Pessoa.[/quote]

Realmente, se houverem novos metodos que o “controller” por exemplo teria que chamar, ia ter que sair refatorando, mas se os metodos continuam os mesmos e mudou apenas a logica do “persist” por exemplo, o polimorfismo daria conta do recado.

Oi fredFerrao,

Estou entendendo que o ponto que está gerando as dúvidas é o fato de que o DAO está diretamente ligado adiferenciado de banco de dados e que o “DAO generico” na verdade precisa “saber” qual é o TIPO de objeto que será persistido, ou seja, na verdade talvez não tenha nada de generico assim, já que ele precisa saber os tipos dos objetos.

A interface específica de cada entidade acaba fazendo parte do padrão de solução (DAO), caso vc tenha mais de um tipo de banco de dados vc “força” que a implementação seja garantida seguindo a especificação da interface.

Resumindo…infelizmente a coisa envolve muito código, acredito que o uso de um bd orientado a objetos estas coisas desapareçam completamente.

P.S Desculpem se não entendi direito o tópico.

flws

[quote=fantomas]Oi fredFerrao,

Estou entendendo que o ponto que está gerando as dúvidas é o fato de que o DAO está diretamente ligado adiferenciado de banco de dados e que o “DAO generico” na verdade precisa “saber” qual é o TIPO de objeto que será persistido, ou seja, na verdade talvez não tenha nada de generico assim, já que ele precisa saber os tipos dos objetos.

A interface específica de cada entidade acaba fazendo parte do padrão de solução (DAO), caso vc tenha mais de um tipo de banco de dados vc “força” que a implementação seja garantida seguindo a especificação da interface.

Resumindo…infelizmente a coisa envolve muito código, acredito que o uso de um bd orientado a objetos estas coisas desapareçam completamente.

P.S Desculpem se não entendi direito o tópico.

flws[/quote]

O que estavamos discutindo, é a necessidade dos dois ultimos, ex: PessoaDao e PessoaDaoImpl, que como voce disse acaba fazendo parte da solucao(DAO).

Sobre os bancos de dados especificos, creio que não entram no mérito da discussão, pois levando-se em conta que se esta usando JPA, não haverá implementações especificas para cada BD.

Entendi…

Acredito que ao utilizar a JPA você tenha condições de eliminar ao menos a interface (PessoaDao) porem a classe PessoaDaoImpl (teriamos que mudar o nome dela) teria que continuar por causa do tipo da entidade que teriamos que informar ao mecanismo ORM ( entityManager.find(objectClass, id) ) sem falar do local dos algoritmos de manipulação dos objetos especificos daquela entidade.

Outro detalhe em relação a interface é que: se voce estiver utilizando o spring será sempre aconselhado a utilizar uma interface ao invés de uma implementação ao desenvolver suas classes e descreve-las nos seus (do spring) xmls.

flws

[quote=fantomas]Entendi…

Acredito que ao utilizar a JPA você tenha condições de eliminar ao menos a interface (PessoaDao) porem a classe PessoaDaoImpl (teriamos que mudar o nome dela) teria que continuar por causa do tipo da entidade que teriamos que informar ao mecanismo ORM ( entityManager.find(objectClass, id) ) sem falar do local dos algoritmos de manipulação dos objetos especificos daquela entidade.

Outro detalhe em relação a interface é que: se voce estiver utilizando o spring será sempre aconselhado a utilizar uma interface ao invés de uma implementação ao desenvolver suas classes e descreve-las nos seus (do spring) xmls.

flws

[/quote]

Não é apenas para o Spring, usar a interface garante desacoplamento mesmo se por exemplo, você não puder usar a JPA nessa entidade em específico, por exemplo, se você divide os dados em vários bancos e certas entidades estiverem em SGBD’s que não têm suporte para JPA, ou se os dados de alguma entidade forem obtidos/manipulados através de serviços web (imagine uma aplicação que faça uso da API de redes sociais como o Twitter).
Existe chance de você precisar de toda essa flexibilidade?
Bom aí é muito de cada um, mas eu acho que é sempre melhor ser o mais correto possível desde o começo.

Opa, estou com o mesmo problema do nosso colega fredferrao.
Vamos lá.
Tenho implementado o seguinte:
Pessoa (entity com geters e seters da tabela)
Dao Generico

import java.io.Serializable;
import java.util.List;
import java.util.Map;
public interface DaoGenerico<T, ID extends Serializable> {	
	public Class<T> getObjectClass();
	public T save(T object);
	public T searchById(ID id) throws Exception;
	public T update(T object);
	public void delete(T object);
	public List<T> retrieve();
	public List<T> searchByParam(String query, Map<String, Object> params);
	public List<T> searchByParam(String query, Map<String, Object> params, int maximo, int atual);
	public List<T> searchPesq(String query);
	public T searchPesq(String query, Map<String, Object> params);
}

Tenho criado também meu DaoGenericoImp

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import br.com.xxx.dao.DaoGenerico;

@Transactional(readOnly=true, propagation=Propagation.REQUIRED)
public class DaoGenericoImp <T, ID extends Serializable> implements DaoGenerico<T, ID>{

	private EntityManager entityManager;
	private final Class<T> oClass;
	
	public Class<T> getObjectClass(){
		return this.oClass;
	}
	
	@PersistenceContext
	public void setEntityManager(EntityManager em){
		this.entityManager = em;
	}
	
	protected EntityManager getEntityManager(){
		if(entityManager == null)
			throw new IllegalStateException("Erro #1");
		return entityManager;		
	}
	
	@SuppressWarnings("unchecked")
	public DaoGenericoImp(){
		this.oClass = (Class<T>)
		((ParameterizedType)getClass().getGenericSuperclass()).
							getActualTypeArguments()[0];
	}
	
	@Transactional(readOnly=false, propagation=Propagation.REQUIRED)
	public T update(T object){
		getEntityManager().merge(object);
		return object;		
	}
	
	@Transactional(readOnly=false, propagation=Propagation.REQUIRED)
	public void delete(T object){
		object = getEntityManager().merge(object);
		getEntityManager().remove(object);
	}
}

Após crio um interface para a Pessoa

import br.com.xxx.dao.DaoGenerico;
import br.com.xxx.entities.Pessoas
public interface IPessoas extends DaoGenerico<Pessoas, Integer> {

}

Crio também a PessoasImp

import br.com.xxx.entities.Pessoas
import br.com.xxx.interfaces.IPessoas;

public class PessoasDaoImp extends DaoGenericoImp<Pessoas, Integer> implements
		IPessoas {

}

E por fim meus beans PessoasMB que é neste ponto onde estou querendo chegar.
Bom toda a implentação dos meus beans tenho que fazer quase sempre a mesma coisa, por exemplo:
Tenho em todos eles os metodos save, delete, cancel, edit
Neste caso estava querendo criar alguma coisa generica tipo implementar estes metodos apenas uma unica vez e sempre que criar um novo bean estes metodos já estariam prontos, caso eu fosse querer fazer alguma coisa a mais ai sim implementava o metodo com estas novos coisas a mais.

Exemplo:
PessoasMB

private Pessoas pessoa;
	
@Resource
private DaoGenerico<Pessoas, Integer> pessoasDao;

public String edit() {
	Pessoas ag = getFromEdit();
	setPessoas(ag);
	return "";
}

public String delete() {
	Pessoas ag = getFromEdit();
	pessoasDao.delete(ag);
	cancel();
	return "";
}

O bean seria mais ou menos isto, claro com mais coisas implementadas, porém todo o bean que implementar tenho que fazer sempre isto, no caso fica muito código redundante.

Alguem pode me dar umas dicas de como posso fazer ou o que fazer para melhorar?

No teu caso voce esta querendo fazer um ManagedBean generico certo?

Não sei se seria possivel, teria que ver se o JSF faria o trabalho, pq ele que vai instanciar este MB generico.

Só testando, tu pode seguir a mesma logica do DaoGenerico.

Não curto DAO genérico… e acredito que os dãos devem ser agrupados e não independentes das entidades, ou seja,
ao invés de:

   UserDao
   RuleDao
   CategoryDao

Usaria:

   AdministrationDao

Depois volto e leio o tópico inteiro para dar continuidade, agora estou sem paciência :slight_smile:
rs
Abraços…

[quote=lelodois]Não curto DAO genérico… e acredito que os dãos devem ser agrupados e não independentes das entidades, ou seja,
ao invés de:

   UserDao
   RuleDao
   CategoryDao

Usaria:

[code]
AdministrationDao

[code]

Depois volto e leio o tópico inteiro para dar continuidade, agora estou sem paciência :slight_smile:
rs
Abraços…
[/quote]
Legal, o Paulo Silveira tinha dito sobre agrupar, mas sumiu do tópico :roll:

Volta ai, e se possivel poste um exemplo de como voce faz os seus DAOS.

Valeu.