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?

Você gostou deste post? Compartilhe:
Se você gostou deste post, talvez você queira assinar o feed RSS! Você também pode me seguir no Twitter.

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.

    • |

      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

      • |

        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.

        • |

          Ops…
          deu algum erro na hora de postar.
          segue novamente o applicationContext.xml.

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

          –>

      • |

        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.
        //

  2. |

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

  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!

  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

  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.

  6. |

    Ok Anderson…

    Qualquer coisa é só perguntar…

  7. |

    deu erro denovo.

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

    –>

  8. |

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

    valeu

  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.

  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.

    • |

      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.

  11. |

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

  12. |

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

  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?

  14. |

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