Blog do Urubatan
msgbartop
Desenvolvedor, Palestrante, Escritor, Nerd Assumido e Pai do Marcus :D
msgbarbottom

27 Dec 06 DAO Generico – um exemplo a pedidos

Bom, no blog da Caelum a um tempo atras fizeram um POST sobre um DAO genérico, meio bizarro
Bom, eu uso um DAO genérico a algum tempo, mas o meu DAO genérico tm algumas features a mais do que o mostrado no post da caelum …

Neste exemplo eu ja deixo os metodos para fazer um query by example prontinho, usando a API de criteria do Hibernate …

bom, chega de enrolação, vamos a alguns exemplos …

primeiro, crio uma interface para este DAO …

package br.com.techoffice.site.dao;

import java.io.Serializable;
import java.util.List;

public interface TOBaseDao<T, PK extends Serializable> {
    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);
    public void rebind(T object);
}

onde o método getObjectClass() deve retornar a classe de trabalho deste DAO, a mesma passada no parâmetro T quando a interface for estendida …

e a implementação desta interface, fica mais ou menos assim:

package br.com.techoffice.site.dao.impl;

import java.io.Serializable;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Example.PropertySelector;
import org.hibernate.type.Type;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import br.com.techoffice.site.dao.TOBaseDao;
import br.com.techoffice.site.dao.TODaoListener;

@Transactional(propagation = Propagation.REQUIRED, timeout = 20)
public abstract class TOBaseHibernateDao extends HibernateDaoSupport implements TOBaseDao<T, PK>, PropertySelector {
    private static final Log logger = LogFactory.getLog(TOBaseHibernateDao.class);
    private final Class objectClass;

    public TOBaseHibernateDao(final Class objectClass) {
        this.objectClass = objectClass;
    }

    @SuppressWarnings("unchecked")
    public Class getObjectClass() {
        return objectClass;
    }

    public int findByExamplePageCount(final T example) {
        final List l = findByExample(example);
        final Integer i = new Integer(l.size());
        return i.intValue();
    }

    public int listAllPageCount() {
        final List l = listAll();
        final Integer i = new Integer(l.size());
        return i.intValue();
    }

    /*
     * (non-Javadoc)
     *
     * @see br.ufrgs.hcpa.template.dao.GetNetBaseDao#findOneByExample(T)
     */
    public T findOneByExample(final T example) {
        final List res = findByExample(example, 0, 1);
        if ((res != null) && (res.size() == 1)) {
            return res.get(0);
        } else {
            return null;
        }
    }

    public T save(final T object) {
        try {
            final Session s = getSession(false);
            s.save(object);
            s.flush();
            return object;
        } catch (final HibernateException ex) {
            TOBaseHibernateDao.logger.error(ex);
            throw convertHibernateAccessException(ex);
        }
    }

    public void update(final T object) {
        try {
            final Session s = getSession(false);
            s.update(object);
            s.flush();
        } catch (final HibernateException ex) {
            TOBaseHibernateDao.logger.error(ex);
            throw convertHibernateAccessException(ex);
        }
    }

    public void rebind(final T object) {
        try {
            getSession(false).refresh(object);
        } catch (final HibernateException ex) {
            TOBaseHibernateDao.logger.error(ex);
            throw convertHibernateAccessException(ex);
        }
    }

    public void delete(final T object) {
        try {
            getSession(false).delete(object);
        } catch (final HibernateException ex) {
            TOBaseHibernateDao.logger.error(ex);
            throw convertHibernateAccessException(ex);
        }
    }

    @SuppressWarnings("unchecked")
    public T load(final PK primaryKey) {
        try {
            final Session s = getSession(false);
            final Object o = s.load(objectClass, primaryKey);
            return (T) o;
        } catch (final HibernateException ex) {
            TOBaseHibernateDao.logger.error(ex);
            throw convertHibernateAccessException(ex);
        }
    }

    @SuppressWarnings("unchecked")
    public T get(final PK primaryKey) {
        try {
            final Session s = getSession(false);
            final Object o = s.load(objectClass, primaryKey);
            return (T) o;
        } catch (final HibernateException ex) {
            TOBaseHibernateDao.logger.error(ex);
            throw convertHibernateAccessException(ex);
        }
    }

    @SuppressWarnings("unchecked")
    public List listAll() {
        try {
            final Session s = getSession(false);
            final Criteria c = s.createCriteria(objectClass);
            addOrderToCriteria(c);
            return c.list();
        } catch (final HibernateException ex) {
            TOBaseHibernateDao.logger.error(ex);
            throw convertHibernateAccessException(ex);
        }
    }

     @SuppressWarnings("unchecked")
    public List findByExample(final T example) {
        try {
            final Session s = getSession(false);
            final Criteria c = s.createCriteria(objectClass);
            c.add(Example.create(example).enableLike(MatchMode.ANYWHERE).ignoreCase().setPropertySelector(this));
            addOrderToCriteria(c);
            addPropertiedToCriteria(c, example);
            return c.list();
        } catch (final HibernateException ex) {
            TOBaseHibernateDao.logger.error(ex);
            throw convertHibernateAccessException(ex);
        }
    }

    protected void addPropertiedToCriteria(final Criteria c, final T example) {
    }

    @SuppressWarnings("unchecked")
    public List findByExample(final T example, final int first, final int max) {
        try {
            final Session s = getSession(false);
            final Criteria c = s.createCriteria(objectClass);
            c.add(Example.create(example).enableLike(MatchMode.ANYWHERE).ignoreCase().setPropertySelector(this));
            addPropertiedToCriteria(c, example);
            addOrderToCriteria(c);
            if (first != 0) {
                c.setFirstResult(first);
            }
            if (max != 0) {
                c.setMaxResults(max);
            }
            return c.list();
        } catch (final HibernateException ex) {
            TOBaseHibernateDao.logger.error(ex);
            throw convertHibernateAccessException(ex);
        }
    }

    @SuppressWarnings("unchecked")
    public List listAll(final int first, final int max) {
        try {
            final Session s = getSession(false);
            final Criteria c = s.createCriteria(objectClass);
            addOrderToCriteria(c);
            if (first != 0) {
                c.setFirstResult(first);
            }
            if (max != 0) {
                c.setMaxResults(max);
            }
            return c.list();
        } catch (final HibernateException ex) {
            TOBaseHibernateDao.logger.error(ex);
            throw convertHibernateAccessException(ex);
        }
    }

    protected void addOrderToCriteria(Criteria c) {
    }

    public boolean include(Object propertyValue, String propertyName, Type type) {
        if((propertyValue!=null) && (propertyValue instanceof String)){
            return !"".equals(((String)propertyValue).trim());
        }
        return propertyValue!=null;
    }
}

onde o método addOrderToCriteria, permite que subclasses deste DAO ordenem as consultas …
o método addPropertiedToCriteria permite que subclasses adicionem propriedades não incluidas pelo hibernate nas consultas …
e o método include combinado com o setPropertySelector da classe Example é uma melhoria adicionada a pouco tempo, que faz o mesmo que o addPropertiedToCriteria, mas de forma automática, ou seja, inclui as propriedades PK e FK também na QBE …

Bom, depois disto tudo, só falta criar o DAO para alguma entidade persistida pelo Hibernate, como por exemplo uma classe Curriculo …

Criamos a interface do nosso DAO:

package br.com.techoffice.site.dao;

import br.com.techoffice.site.data.Curriculo;

public interface CurriculoDao extends TOBaseDao{
}

e logo depois a implementação deste DAO:

package br.com.techoffice.site.dao.impl;

import net.java.dev.springannotation.annotation.Bean;
import br.com.techoffice.site.dao.CurriculoDao;
import br.com.techoffice.site.data.Curriculo;

@Bean(name="curriculoDao")
public class CurriculoDaoImpl extends TOBaseHibernateDao implements CurriculoDao {
	private static final long serialVersionUID = 1L;

	public CurriculoDaoImpl() {
        super(Curriculo.class);
    }
}

acho que era isto, o que acharam deste DAO genérico?
pela minha experiência, ele resolve de maneira estupidamente fácil em torno de 60% dos meus problemas de persistencia, nos outros 40% ai é necessário criar algum outro metodo de pesquisa especializado para cada situação …

mas acho que é esta a ideia das coisas genéricas, facilitar a maior parte do trabalho e permitir que a menor parte seja feita com um pouquinho de trabalho extra :) o que vocês acham?

If you enjoyed this post, make sure you subscribe to my RSS feed!

Tags:

Reader's Comments

  1. |

    Bom dia Francisco,

    No meu caso eu tenho um DAO genérico que contém apenas os métodos que são comuns a todas as entidades, o mesmo é abstrato.

    Os demais daos herdam o dao genérico.

    Porém por questões de manutenabilidade e também por utilizar o spring, o struts 2 e o hibernate (obs: acredito que nenhum dos frameworks obriga o uso de interface), todas as minhas daos possuem uma interface e nas Actions (Struts 2) eu delcaro as minhas instâncias com a Interface e o Spring se encarrega de criar a intância correspondente e injetá-lo na Action.

    import scfc.model.dao.ChamadoDAO;

    import com.opensymphony.xwork2.Action;
    import com.opensymphony.xwork2.ActionSupport;
    import com.opensymphony.xwork2.Preparable;

    public class ManterOrdemServico extends ActionSupport implements Preparable, SessionAware{

    //interface
    private ChamadoDAO chamadoDao;

    //…
    }

    Desse modo, caso eu queira mudar a implementação do meu dao generico basta criar uma nova classe que implemente a interface da minha entidade e que herde a nova implementação da classe genérica. E no xml de configuração do spring, basta mudar a classe para a nova.

    Isso facilita mto, pois esse tipo de alteração torna-se transparente para o restante do sistema.

    public interface GenericDAO {

    void save(T entity);

    void update(T entity);

    void remove(T entity);

    T find(ID id);

    List findAll();

    T getReference(ID id);

    }

    @Transactional
    public abstract class GenericImplDAO implements GenericDAO{

    private Class persistentClass;

    @PersistenceContext
    private EntityManager em;

    @SuppressWarnings(“unchecked”)
    public GenericImplDAO(){
    this.persistentClass = (Class)((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public void setEm(EntityManager em) {
    this.em = em;
    }

    protected EntityManager getEm() {
    return em;
    }

    public Class getPersistentClass() {
    return persistentClass;
    }

    @Override
    public void save(T entity) {
    em.persist(entity);
    }

    @Override
    public void update(T entity){
    em.merge(entity);
    }

    @Override
    public void remove(T entity) {
    em.remove(entity);
    }

    @Override
    public T find(ID id) {
    return em.find(getPersistentClass(), id);
    }

    @Override
    public List findAll() {
    return findByCriteria();
    }

    @SuppressWarnings(“unchecked”)
    protected List findByCriteria(Criterion… criterion) {

    Session session = (Session) em.getDelegate();

    Criteria crit = session.createCriteria(getPersistentClass());

    for (Criterion c : criterion) {
    crit.add(c);
    }

    return crit.list();
    }

    @SuppressWarnings(“unchecked”)
    protected List findByCriteria(Order o, Criterion… criterion) {

    Session session = (Session) em.getDelegate();

    Criteria crit = session.createCriteria(getPersistentClass());

    for (Criterion c : criterion) {
    crit.add(c);
    }

    crit.addOrder(o);

    return crit.list();
    }

    @Override
    public T getReference(ID id) {

    return getEm().getReference(getPersistentClass(), id);

    }

    }

    public interface ServicoDAO extends GenericDAO {

    List findAllForContrato(Categoria cat);

    }

    public class ServicoImplDAO extends GenericImplDAO implements ServicoDAO {

    @Override
    public List findAllForContrato(Categoria categoria) {

    Conjunction c = Restrictions.conjunction();

    c.add(Expression.eq(“contrato”, true));

    c.add(Expression.eq(“categoria”, categoria));

    return findByCriteria(c);

    }

    }

    Espero ter ajudado.

    Reply to this comment
    • |

      Raphael, você poderia dar mais detalhes da sua integração entre Struts2 + Spring + Hibernate(JPA) ao qual se referiu no seu post? Poderia postar o applicationContext?
      orbigado,
      Leonardo

      Reply to this comment
      • |

        Exemplo de um applicationContext.xml.

        <!– Alternativa caso queira configurar a conexão com o banco direto da aplicação

        –>

        Na documentação do Spring mostra outras alternativas para algumas configurações.
        No site do Struts 2 (versão 0) tem um exemplo de intregralção entre eles, há também um exemplo de integração no site da revista java magazine.

        Mas se sugirem dúvidas posta ai.

        Reply to this comment
      • |

        Troquei o símbolo de comentário do xml por //, vamos ver se agora vai.

        // Usa o JNDI configurado no Container

        // Alternativa caso queira configurar a conexão com o banco direto da aplicação
        //
        //
        //
        //
        //
        //

        // Atentar-se para a propriedade database, ela corresponder ao seu banco.
        // Veja na documentação do spring qual o valor que você deve colocar.
        //

        Reply to this comment
  2. |

    Muito obrigado ajudou sim, e esclareceu bastante dúvidas…vlw cara.

    Reply to this comment
  3. |

    Olá urubatan, esse tutorial foi feito em dezembro de 2006. Até hoje tenho procurado alguma coisa mas recente sobre isso e não tenho encontrado. O que estou querendo é exatamente as configurações do hibernate no meu applicationContext mas só enconto artigos antigos. Tem algo mais recente que possa me indicar?

    Grato!

    Reply to this comment
  4. |

    Anderson,

    Quanto as configurações do hibernate no applicationContext, você pode criar um datasource no context.xml e pegar a conexão via jndi no applicationContext

    Reply to this comment
  5. |

    Olá Gessé,

    Algumas coisas ainda são meio obscuras para mim, mas nada que não possa ser resolvido. Já estou quase conseguindo, de qualquer forma agradeço pela força.

    Reply to this comment
  6. |

    Ok Anderson…

    Qualquer coisa é só perguntar…

    Reply to this comment
  7. |

    deu erro denovo.

    <!– Alternativa caso queira configurar a conexão com o banco direto da aplicação

    –>

    Reply to this comment
  8. |

    Cara como fica isso no spring ? O genericDao tem que ter alguma configuracao especifica ?

    valeu

    Reply to this comment
  9. |

    O genericDao não, mas o hibernate sim.

    No momento ainda não consegui configurar no spring, mas estou utilizando uma classe utilitaria temporaria e espero jogar isso pro spring em breve.

    Reply to this comment
  10. |

    Lembrando que esse exemplo foi criado a partir de: http://www.ibm.com/developerworks/java/library/j-genericdao.html

    É sempre bom citar a fonte de onde se copia a informação.

    Reply to this comment
    • |

      Na verdade o código é semelhante mas o meu não foi baseado neste não, a idéia eu tive partindo do exemplo postado no blog da caelum como cito no início do post, só li este link que você enviou depois de já estar utilizando o meu Dao genérico por algum tempo.
      Mas obrigado pelo link.

      Reply to this comment
  11. |

    cmo ficaria a integracao desse dao generico com o spring, no caso o spring +hibernate.. ?

    Reply to this comment
  12. |

    Poxa, achei que não estã tão completo…
    Foi o primeiro resultado da pesquisa do google…
    :(

    Reply to this comment
  13. |

    Estava procurando por um DAO genérico e até agora foi o melhor que eu achei.

    Porém estou com um problema de implementação.
    Quando tento rodar aparece o erro:

    “java.lang.IllegalArgumentException: No SessionFactory specified
    at org.springframework.util.Assert.notNull(Assert.java:112)
    at org.springframework.orm.hibernate3.SessionFactoryUtils.doGetSession(SessionFactoryUtils.java:281)
    at org.springframework.orm.hibernate3.SessionFactoryUtils.getSession(SessionFactoryUtils.java:200)
    at org.springframework.orm.hibernate3.support.HibernateDaoSupport.getSession(HibernateDaoSupport.java:165)
    at mignard.dao.impl.TOBaseHibernateDao.listAll(TOBaseHibernateDao.java:136)

    Saberia me dizer o erro ou o que tenho que fazer para funcionar?

    Reply to this comment
  14. |

    Tirei a parte do spring e mudei o get session e esta funcionando =)

    Reply to this comment

Leave a Comment