quinta-feira, 5 de abril de 2007

Data Access Object Generic


Esse artigo explora as classes envolvidas no DAO do ITrust. O DAO que iremos usar é implementado com Hibernate e JDK5, muita gente acha desnecessário implementar DAO com Hibernate, portanto mostramos aqui o porque dessa implementação. O DAO do ITrust encontra-se dentro do package org.iprogramming.model.persistence.dao e nele encontramos as sepaçoes de packages descritas abaixo

|---+dao
| |
| |---generic
| |---hibernate

Dentro do package dao nos iremos adicionar todas as nossas interfaces de DAO, estas interfaces irão abstrair qual a forma de acesso ao banco de dados e serve para especificar quais os metodos padrões que um DAO deve possuir. Porem todos os DAOs vão possuir metodos semelhantes no caso do hibernate, sendo assim, iremos criar primeiro um GenericDAO para pouparmos um certo trabalho, portanto a interface GenericDAO sera o nosso exemplo e ainda terá dupla funcionalidade descrita posteriormente

public interface GenericDAO <T, ID extends Serializable>{
public T findById(Class clazz, ID id);
public List list(Class clazz);
public List list(Class clazz, int firstResult, int maxResults);
public List listByExample(T entity);
public T makePersistent(T entity);
public void makeTransient(T entity);
}

Devemos observar que esta interface será implementada em todas as demais interfaces, e isso so foi possivel através do recurso Generic implementado no JDK5, porem cada DAO possui as suas peculiaridades, e para criarmos as peculiaridades de cada DAO nos vamos simplismente criar nossos DAOs extendendo a GenericDAO e adicionando os metodos peculiares, segue exemplo abaixo.

interface LoginDAO extends GenericDAO<Login, Long>{
public Login findLoginByNamedQuery(Login arg);
}

Note que ao extendermos a interface GenericDAO nos precisamos especificar os tipo genericos atribuidos a GenericDAO, para vc entender melhor imagine que no lugar T e ID será substituido por Login e Long. Depois nos criamos um metodo especifico para o DAO Login, sendo assim, nao precisamos ficar escrevendo os metodos padroes como insert, delete, update e list.


Agora que ja possuimos a nossa interface para o DAO de Login nos precismos de fato criar uma classe que implementa esta interface, porem agora vamos descrever algo muito peculiar do hibernate. Nos vamos criar um GenericHibernateDAO para abastrair os metodos de insert, delete, update e list, isso tambem é possivel usando o recurso de Generic do jdk5. Segue abaixo o nosso exemplo:

public class GenericHibernateDAO <T, ID extends Serializable, DAOImpl extends GenericDAO<T, ID>> implements GenericDAO<T, ID> {
private Session session;

public DAOImpl setSession(Session s) {
this.session = s;
return (DAOImpl)this;
}

protected Session getSession() {
if (session == null)
throw new IllegalStateException("Session has not been set on DAO before usage");
return session;
}

public T findById(Class clazz, ID id) {
return (T)getSession().get(clazz, id);
}

public List list(Class clazz) {
return getSession().createCriteria(clazz).list();
}

public List list(Class clazz, int firstResult, int maxResults) {
Criteria criteria = getSession().createCriteria(clazz);
criteria.setFirstResult(firstResult);
criteria.setMaxResults(maxResults);
return criteria.list();
}

public List listByExample(T entity) {
Criteria criteria = getSession().createCriteria(entity.getClass());
Example sample = Example.create(entity);
sample.enableLike();
sample.excludeZeroes();
criteria.add(sample);
return criteria.list();
}

public T makePersistent(T entity) {
getSession().saveOrUpdate(entity);
return entity;
}

public void makeTransient(T entity) {
getSession().delete(entity);
}
}

A classe GenericHibernateDAO é o cerne de todos os nossos DAOs, pois este ira conter todas as rotinas basicas para as nossas tabelas no banco de dados. Ainda podemos perceber a segunda funcionalidade da Inteface GenericDAO, pois agora a interface esta sendo implementada ao inves de extendida. Não perca tempo imaginando o que é isso <T, ID extends Serializable, DAOImpl extends GenericDAO<T, ID>> implements GenericDAO<T, ID> Apenais saiba que o conceito aqui é o mesmo descrito para a interface GenericDAO, ou seja, isto são tipos genericos que serão especificados quando alguem extender essa classe


Agora vc deve entender o uso do Generics em nossos DAOs, pois ao extender a classe GenericHibernateDAO nos especificamos quais sao os atributos que a classe 'Pai' deve usar. Note que nao precisamos escrever as demais rotinas de insert, delete, update e list. Agora tente implementar outro DAO e vc irá perceber que vc so precisar criar algumas classes e extender outras, se vc ja criou DAOs usando JDBC vc irá entender porque o hibernate é uma ótima ferramenta.

public class HibernateLoginDAO extends GenericHibernateDAO<Login, Long, LoginDAO> implements LoginDAO {
public Login findLoginByNamedQuery(Login arg)
Query select = getSession().getNamedQuery("findUser");
select.setLong("pkUsers", arg.getPkLogin());
select.setString("roleName", arg.getRoles().iterator().next().getName());

List<Login> logins = (List<Login>) select.list();
for(Login login : logins)
return login;

return null;
}
}


Terminamos aqui os nossos DAOs, veja abaixo o UML e estude, pois no proximo artigo vamos descrever como vc deve criar as instancia de seus DAOs e como controlar as transacoes do hibernate, provavelmente vc deve estar se perguntando: O que sao os metodos setSession e getSession na classe GenericHibernateDAO. Esses metodos serão descritos no proximo artigo.


Um comentário:

Anônimo disse...

Ronildo,

Tenho algumas sugestões sobre a definição do teu DAO genérico. Primeiro, gostaria de sugerir a execução dos refactorings: (i) rename interface e rename method, com o objetivo de melhorar a legibilidade da interfaces. Por exemplo. DAO ao invés de GenericDAO ou IDAO; save, delete, listAll

Já que você está utilizando Generics, não há necessidade dos métodos: findById,list(Class clazz) e list(Class clazz, int firstResult, int maxResults) receberem como argumento a classe, haja vista, que ela ser do mesmo tipo do parâmetro (T) da interface e ela pode ser obtida por reflexão. Além disso, sugiro que as listas(java.util.List) sejam parametrizadas com o tipo da interface.

Conforme mencionei no início do comentário, as mudanças são apenas sugestões.