Ei! Como é o seu DAO? Ele é tão abstraído quanto o meu?

Essa é uma pergunta comum entre os desenvolvedores. Alguns acham que há uma fórmula única, que DAO é um pattern fechado e que possui seu diagrama de classes bem definido. Eu discordo.

Na minha humilde opinião, DAO é uma maneira de você encapsular o seu acesso a dados. Não importa se é através de uma factory de objetos que recebem uma Session do hibernate por injeção de dependências, ou se é um montão de métodos estáticos cheio de SQL dentro deles (argh!). Obviamente a última opção envolve diversos outros antipatterns (como singleton e métodos estáticos, que já criticamos anteriormente), porém ela consegue isolar seu acesso a dados, cumprindo o papel do DAO e de alguma maneira organizando sua camada.

Lendo a definição de Data Access Object no site da Sun, podemos chegar a um molde comum, mas nada impede que possamos incrementa-lo e modifica-lo de acordo com nossas necessidades. Por exemplo, com o Java 5, podemos tirar proveito dos generics. Na Caelum costumamos usar uma interface para os nossos DAOs parecida com a seguinte:

interface Dao<T> {
  T search(Serializable id);
  void persist(T t);
  void remove(T t);
  // ...
}

A nossa implementação de DAO genérico para hibernate fica parecida com:

class GenericHibernateDao<T> implements Dao<T> {
  private Session session;
  private Class persistentClass;

  public GenericHibernateDao(Session session, Class persistentClass) {
    this.session = session;
    this.persistentClass = persistentClass;
  }

  public T search(Serializable id) {
     return session.load(persistentClass, id);
  }

  // outros metodos da interface Dao
}

Repare que ele recebe session como argumento, que poderia ser injetada por um container. Outra opção seria buscar a session de um singleton (o clássico HibernateUtil), mas a primeira maneira é bem mais elegante e testável. Repare que ainda temos os problemas de controle de transações: onde abrimos e comitamos? dentro do Dao? de dentro do injetor de dependências? de dentro da sua lógica de negócios? Nós costumamos fazer esse controle em um interceptador ou mesmo em um Filter de servlets, dependendo do framework MVC usado. Nosso filtro é semelhante com o recomendado pela documentação do hibernate.

E então podemos utilizar nosso DAO:

Dao<Cliente> clienteDao = new GenericHibernateDao<Cliente>(session);
clienteDao.persist(cliente);
Cliente c5 = clienteDao.search(5);

Para as entidades que você precisa de mais que um simples cria/le/atualiza/deleta, criamos uma nova interface:

interface ClienteHibernateDao extends Dao<Cliente> {
   List<Cliente> buscaTodosClientesQueDevemDinheiro();
}

E teríamos uma classe ClienteHibernateDao que estende GenericHibernateDao<Cliente> e implementa ClienteDao.

Meus colegas da easytech preferem abstrair também o tipo da chave primária:

interface Dao<T, I> {
  T search(I id);
  // ...
}

Dessa maneira você realmente ganha mais tipagem, já que no DAO anterior poderíamos passar qualquer objeto serializável como chave. Podemos refinar muito mais nossas classes de acesso a dados, mas até que ponto chegar?

Ficar abstraindo demais a sua camada de persistência pode levar a proliferação de centenas de pequenas classes que simplesmente não fazem nada, só delegam e delegam. Claro que existem casos em que abstrair para apenas delegar é interessante, diminuindo o acoplamento entre suas classes, porém isso pode chegar a um ponto que vai atrapalhar o desenvolvimento.

Nesta entrada do blog dos desenvolvedores do hibernate, o uso de camadas para abstrair as ferramentas de mapeamento objeto relacional são duramente criticadas.

E você, qual é a receita de bolo para o seu DAO? Até onde você o refina?

34 Comentários

  1. Urubatan 27/08/2006 at 01:27 #

    eu uso um bem parecido com isto :D

    mas sigo mais a linha do pessoal a easytech como você comentou, ja com a classe da PK abstraida, e no meu DAO ja tenho metodos find by example prontos também …

    public interface TOBaseDao {
    	public Class getObjectClass();
    	public T save(T object);
    	public T load(PK primaryKey);
    	public T get(PK primaryKey);
    	public List listAll();
    	public List findByExample(final T example);
    	public T findOneByExample(final T example);
    	public List listAll(final int first,final int max);
    	public int listAllPageCount();
    	public List findByExample(final T example,final int first,final int max);
    	public int findByExamplePageCount(final T example);
    	public void update(T object);
    	public void delete(T object);
    }


    e a implementação padrão dele fica parecido com isto:

    public class EnderecoEmailDaoImpl extends TOBaseHibernateDao implements EnderecoEmailDao {
    
        public EnderecoEmailDaoImpl() {
            super(EnderecoEmail.class);
        }
    
        @Override
        protected void addPropertiedToCriteria(final Criteria c, final EnderecoEmail example) {
            if (example.getCliente() != null) {
                c.add(Property.forName("cliente").eq(example.getCliente()));
            }
        }
    }
    


    O método addPropertiedToCriteria serve para contornar o problema do findByExample do Hibernate não considerar campos de relacionamento com outras entidades nos filtros … assim só com isto eu tenho um cadastro simples prontinho em 20 minutos :D

    Mas cada doido com as suas manias …

  2. Ederson de Lima 28/08/2006 at 14:37 #

    Muito bom,
    e gostei da sua definição do DAO, utilizalo como encapsulamento o seu acesso aos dados.

    Muito bom..

  3. Michael Nascimento Santos 28/08/2006 at 15:08 #

    Por que não declarar persistentClass como Class<T> ou Class<? extends T>?

  4. Paulo Silveira 28/08/2006 at 15:20 #

    É verdade Michael, dessa maneira poderíamos usar o @SurpressWarnings(“unchecked”) nos métodos sem correr risco algum…

    Já usar com o ? extends T não funciona, pois alguém pode criar um new Dao(session, FilhaDeFuncionario.class) e passar outras filhas de Funcionario como argumento, dando erro em alguns métodos que usem o findByExample, entre outros (já que o criteria seria criando com FilhaDeFuncionario.class e o Example seria criado em cima de uma instância de outra filha).

  5. Paulo Silveira 28/08/2006 at 15:28 #

    Dando uma pesquisada, vi que na documentação do hibernate tem soluções muito parecidas com a de todos nós:

    http://www.hibernate.org/328.html

    Como o Michael indicou para o caso do Class<T>, devemos também restringir a chave primária para ser compatível com Serializable:

    
    interface Dao<T, ID extends Serializable> {
    }
    

  6. Fabio Kung 29/08/2006 at 19:06 #

    Ultimamente, andei pensando muito num assunto muito relacionado a tudo isso: a herança que a gente usa quando estamos escrevendo nossos daos…


    public class UsuarioDAO extends GenericDAO implements UsuarioDAO { ... }


    Se pararmos para pensar, esse é um exemplo de mau uso da herança. Estamos herdando sem usar o polimorfismo! Herdamos de GenericDAO só por conveniência, para ganhar alguns métodos que estão lá.
    Várias vezes já tive vontade de mudar isso e usar um modelinho diferente para os meus DAOs, sem essa herança.
    Na prática, como a herança não me trouxe nenhuma grande dor de cabeça (além de ter que lembrar de estender a classe GenericDAOWithGreatCRUDStuff e implementar a interface UselessWithAtMostThreeMethodsDAO), continuo usando a herança feia! :D

  7. Fabio Kung 29/08/2006 at 19:27 #

    Já quanto a abstrair o framework de mapeamento objeto relacional, eu sou totalmente a favor. Há alguns casos em que a performance do framework não é satisfatória, ou a ferramenta não suporta uma funcionalidade imprescindível do mecanismo final de persistência (banco de dados relacional, neste caso). Neste caso, que é só um exemplo, pode ser muito melhor sacrificar um pouco das vantagens da ferramenta de ORM e acessar o banco de dados direto.

    O melhor de tudo é que o resto da aplicação nem vai saber disso, justamente porque a persistência está isolada. Não importa se é JDBC direto, se é Hibernate, se é Toplink, iBatis, ou se o banco de dados é legado e em um caso específico a persistência teve que ser feita em um arquivo xml.

  8. Paulo Silveira 29/08/2006 at 21:19 #

    Fábio, sobre o DAO você está coberto de razão. Estamos usando herança apenas pra nos livrar de escrever uma linhazinha a mais por método… porque o correto realmente seria a gente delegar as chamadas do persist, search e outros para o GenericHibernateDao. ter um GenericHibernateDao em vez de ser GenericHibernateDao.

  9. Tiago Silveira 31/08/2006 at 04:33 #

    Eu não faço isso com meus DAOs. Eu agrupo a persistência por módulos, sem herança feia, com uma única interface que tem todos os save(), load(), etc.

    Essa abordagem também tem seus problemas, mas estou descobrindo que quanto menos camadas, menos código vc escreve pra atingir o mesmo resultado. Pelo menos no que toca a persistência de POJOs.

  10. Bruno Hansen 17/10/2006 at 09:01 #

    Acho que nesse caso:

    public class UsuarioDAO extends GenericDAO implements UsuarioDAO { … }

    Não há muito problema se o UsuarioDao implementar metodos que ainda não existam no GenericDAO e não subscreva nada de GenericDAO!

    Estava aqui pensando, se agente fosse delegar tudo para o GenericDAO tambem causaria a mau cheiro “Intermediário”

  11. soro 20/10/2006 at 13:51 #

    quando vcs falam em delegar para o GenericDAO em vez d extendê-lo, seria algo assim:

    public class GenericDAO implements DAO{
    ......blablabla
    }

    public class ClienteDAOImpl implements ClienteDAO{

    GenericDAO genDao = new GenericDAO();

    void persist(){
    genDAO.persist(T t);
    }
    .....blablabla....
    }

    ????

  12. Márcio Barroso 22/11/2006 at 10:05 #

    Olá, gostei mto da matéria.
    Como sugerido, segue meus daos. Gostaria de que se por ventura alguém tenha alguma sugestão qto a minha implementação, que a comente por favor.
    Paulo, estou usando a sua sugestão qto ao uso do varargs. :)

    /**
    * BaseDao definition
    */
    public interface IBaseDao {

    public void persist(T object);

    public void delete(T object);

    public void deleteByPrimaryKey(Serializable id);

    public T findByPrimaryKey(Serializable id);

    public Collection findByQuery(String hql);

    public Collection findCriteriaByEntity(T object);

    public Collection findCriteriaByEntityOrder(T object, String… fieldsOrdinance);

    public Collection findCriteria(Criterion[] criterion, Order… order);

    public Collection findAll();
    }

    Implementação :

    public abstract class BaseDaoImpl implements IBaseDao {

    private static Logger logger = Logger.getLogger(BaseDaoImpl.class);

    private Class persistentClass;

    protected SessionFactory sessionFactory;

    protected HibernateTemplate hibernateTemplate;

    public abstract void setSessionFactory(SessionFactory sessionFactory);

    public BaseDaoImpl() {
    logger.debug(“Contrutor default”);
    this.persistentClass = (Class) ((ParameterizedType) getClass()
    .getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public void delete(T object) {
    logger.debug(“Deleting object ” + object.getClass().getSimpleName());
    this.hibernateTemplate.delete(object);
    }

    public void deleteByPrimaryKey(Serializable id) {
    logger.debug(“Deleting ” + persistentClass.getSimpleName()
    + ” by primary key ” + id);
    Object temp = this.hibernateTemplate.get(persistentClass, id);
    this.hibernateTemplate.delete(temp);
    }

    public Collection findAll() {
    logger.debug(“Finding all ” + persistentClass.getSimpleName());
    DetachedCriteria criteria = DetachedCriteria
    .forClass(persistentClass);
    return this.hibernateTemplate.findByCriteria(criteria);
    }

    public T findByPrimaryKey(Serializable id) {
    logger.debug(“Finding ” + persistentClass.getSimpleName()
    + ” by primary key ” + id);
    return (T) this.hibernateTemplate.get(persistentClass, id);
    }

    public Collection findByQuery(String hql) {
    logger.debug(“Find by query ” + hql);
    return this.hibernateTemplate.find(hql);
    }

    public Collection findCriteria(Criterion[] criterion, Order… order) {
    logger.debug(“Finding by criteria”);
    DetachedCriteria criteria = DetachedCriteria
    .forClass(persistentClass);
    for (Criterion crit : criterion) {
    criteria.add(crit);
    }
    for (Order ord : order) {
    criteria.addOrder(ord);
    }
    return this.hibernateTemplate.findByCriteria(criteria);
    }

    public Collection findCriteriaByEntity(T object) {
    logger.debug(“Finding by Entity”);
    DetachedCriteria criteria = null;
    try {
    criteria = mountDynaCriteria(object);
    } catch (Exception e) {
    logger.error(e);
    }
    return this.hibernateTemplate.findByCriteria(criteria);
    }

    public void persist(T object) {
    logger.debug(“Saving ” + persistentClass.getSimpleName());
    this.hibernateTemplate.persist(object);
    }

    public Collection findCriteriaByEntityOrder(T object,
    String… fieldsOrdinance) {
    logger.debug(“Finding by criteria onder”);
    DetachedCriteria criteria = null;
    try {
    criteria = mountDynaCriteria(object);
    for (String ordinance : fieldsOrdinance) {
    criteria.addOrder(Order.asc(ordinance));
    }
    } catch (Exception e) {
    logger.error(e);
    }
    return this.hibernateTemplate.findByCriteria(criteria);
    }

    private DetachedCriteria mountDynaCriteria(Object object) throws Exception {
    logger.debug(“Mounting dynamic criteria for ”
    + persistentClass.getSimpleName());
    Class klass = object.getClass();
    Method[] m = klass.getMethods();
    DetachedCriteria criteria = DetachedCriteria.forClass(klass);
    for (int i = 0; i {

    public void setSessionFactory(SessionFactory sessionFactory) {
    this.sessionFactory = sessionFactory;
    this.hibernateTemplate = new HibernateTemplate(sessionFactory);
    }
    }

    Eu tenho uma implementação genérica mais rica se falando em qtde de métodos, o q acaba economizando tempo na implementação dos meus daos.

    Críticar e/ou sugestões são sempre bem aceitas.
    []’s

  13. Edson 28/11/2006 at 22:32 #

    Abstraído ou Abstrato?

  14. Edson 30/11/2006 at 08:42 #

    Insisto: Abstraído ou ABSTRATO?

  15. Paulo Silveira 30/11/2006 at 10:17 #

    ola Edson. Desculpe, mas nao entendi a insistencia. Voce esta querendo dizer de um possivel erro de portugues?

    Olhando os dicionarios por ai voce pode ver que abstraido está corretissimo:
    http://www.wordreference.com/ptes/abstra%EDdo

    Ou foi algo semantico que nao consegui captar?

  16. Thiago Oliveira 16/04/2007 at 22:40 #

    Galera, sei que o post ficou em 2006, mas gostaria de expor algo (além de dizer que o post é muito bacana!).

    A generalização do DAO é interessante para isolar a regra da persistência (evitar contato direto com o banco).
    Mas existem situações em que a complexidade de uma rotina dentro de um método exige que se use alguma forma mais dinâmica de realizar uma consulta na base.
    E nesses casos acabamos por usar HQL, ou JQPL (JPA), ou uma Criteria, ou até mesmo SQl Nativo, dentro da nossa regra de negócios.

    Nesses casos, o que podemos fazer? Pois isso quebra o isolamento entre a regra de negócio e o mecanismo de persistência (imagine que preciso trocar o hibernate pela JPA.. precisaria rever todas essas consultas dinâmicas).. Existe saída??

    Abraços!

  17. Fabio Kung 17/04/2007 at 09:16 #

    Como esses modos específicos de implementar a consulta devem continuar todos dentro do DAO (e não diretamente na sua lógica de negócios), a solução é reimplementar o DAO.

  18. BARBARA 17/11/2007 at 12:26 #

    Muito interessante,

    Eu utilizo Top Link e gostaria muito de ver um exemplo DAO. Estou desenvolvendo um mas estou tendo alguns probleminhas pois só encontrei exemplos utilizando Hibernate.

  19. Paulo 07/12/2008 at 06:36 #

    Olá poderia me dar um exemplo para essa utilização do DAO ,pois sou iniciante e estou com varias duvidas …

  20. Paulo 07/12/2008 at 06:37 #

    Pois também não entendi onde vou acessar o arquivo persistence.xml com o jpa definido no arquivo ????alguém teria um exemplo pra isso ?????

  21. Marcos 12/02/2009 at 09:13 #

    A pergunta é, vale a pena?

    * Normalmente uma classe possui muito mais metodos relacinados a dados que um CRUD.
    * Agora seu implementador de código pode excluir qualquer coisa acesando diretamente a persistência, isso que dizer que seu código foi desenvolvido para 1 cabeça dar manutenção. Exemplo: excluir uma empresa da base (se não houver Constraint Restrict).
    * Não economizou código, pelo contrario, escreveu mais. Praticamente terá que implementar muitos métodos do objeto session da API Hibernate.

    *Nenhum isolamento de pacote…

    Não existe uma DAO generico no proprio Hibernate? Porque? ;)

  22. Eduardo 19/04/2010 at 19:05 #

    Fala galera, alguem sabe me dizer como ficaria esse codigo todo sem usar o Hibernate?

    Att.

  23. lindemberg 11/11/2010 at 17:04 #

    No meu DAO métodos tambem são objetos, logo existem as interfaces

    interface Ipersist {
    void persist(T t);
    }

    interface Iremove {
    void remove(T t);
    }

    interface Isearch {
    T search(Serializable id);
    }

  24. diego lovison 07/06/2011 at 11:21 #

    Em vez de estender (DaoGenerico) ou implementar (IDaoGenerico) no DaoEspecifico, por que não injetamos o IDaoGenerico(persist, remove) e utilizamos para operações CRUD. E injetamos também o DaoEspecifico (clientesQueDevem, bonsClientes) e utilizamos para tratar questões especificas.

    Porque vocês não fazem assim? O que tem de errado nesse conceito?

    Obrigado ;)

  25. Paulo Silveira 08/06/2011 at 18:10 #

    Ola Diego!

    É uma opção válida. O que costuma acontecer é que o DAO genérico começa a não servir para muita coisa, pois, mesmo em situações simples como o persist, começamos a colocar outros detalhes de persistência dentro desses métodos, relativos a “lógica de persistência”. A partir desse momento o dao generico nao ajuda muito, nao importa se fizermos elegantemente via injecao de dependencias e sem heranca.

  26. Diego Lovison 11/06/2011 at 11:28 #

    Obrigado pela sua resposta Paulo.

    Foi o que acabou acontecendo, eliminamos a implementação do DAO Genérico e, utilizamos somente a interface genérica que passou a ser implementada por cada DAO especifico.

    Um motivo disso foi que, em alguns momentos, as informações eram persistidas em arquivos. O fato de invocar o método persist do DAOGenérico sempre, havia momentos em que deveria ser invocado o metódo persist do DAOEspecifico. Isso iria acabar causando confusão para o desenvolvedor. Além de ficar uma arquitetura “confusa”.

    Eliminamos a palavra/conceito de DAO e adotamos a de Repository ;)

  27. Ricardo 20/03/2013 at 17:05 #

    Sei que isto ai funciona muito bem para JPA, Hibernate e tals,
    mas como seria se eu quisesse implementar isto em lista, por exemplo. Como uma lista poderia implementar um interface tão generica quanto esta???

  28. Carl Edwin 07/05/2014 at 12:33 #

    Paulo,
    Meus parabéns, pelo post! Muito bom!!!

  29. Gabriel Simas 29/08/2014 at 15:55 #

    Olá amigos.

    eu tb faço assim

    interface Dao {
    }

    Desde que aprendi a utilizar o DAO…

Deixe uma resposta