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 TOBaseHibernateDaoextends 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 TOBaseHibernateDaoimplements 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?
Tags: Java
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.
[Translate]
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
[Translate]
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.
[Translate]
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
–>
[Translate]
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.
//
[Translate]
Muito obrigado ajudou sim, e esclareceu bastante dúvidas…vlw cara.
[Translate]
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!
[Translate]
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
[Translate]
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.
[Translate]
Ok Anderson…
Qualquer coisa é só perguntar…
[Translate]
deu erro denovo.
<!– Alternativa caso queira configurar a conexão com o banco direto da aplicação
–>
[Translate]
Cara como fica isso no spring ? O genericDao tem que ter alguma configuracao especifica ?
valeu
[Translate]
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.
[Translate]
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.
[Translate]
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.
[Translate]
cmo ficaria a integracao desse dao generico com o spring, no caso o spring +hibernate.. ?
[Translate]
Poxa, achei que não estã tão completo…
Foi o primeiro resultado da pesquisa do google…
[Translate]
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?
[Translate]
Tirei a parte do spring e mudei o get session e esta funcionando =)
[Translate]