DAO Impl com fluent interface :-)

Ola amigos,

Gostaria de compartilhar com voces uma ideia que tive.

Cansado de implementar DAOs com trocentos metodos sobrecarregados, tipo:

List findAll();
List findAll(String orderBy);
List findAll(int firstResult, int maxResults);
List findAllByExample(Example o);
T findOneByExample(Example o);

etc. etc. Sem contar as possiveis combinacoes entre eles…

Resolvi refatorar um velho DAO e surgiu o seguinte:


public class Dao<T, K extends Serializable> {

	protected Criteria c;
	protected Session session;
	protected Class<T> persistentClass;

	public Dao(Session session, Class<T> persistentClass) {
		this.session = session;
		this.persistentClass = persistentClass;
	}
	
	public Dao<T, K> find() {
		c = session.createCriteria(persistentClass);
		return this;
	}
	
	public Dao<T, K> withActiveStatus(Boolean active) {
		c.add(Restrictions.eq("active", active));
		return this;
	}
	
	public Dao<T, K> withOrderByAsc(String propertyName) {
		c.addOrder(Order.asc(propertyName));
		return this;
	}
	
	public Dao<T, K> withOrderByDesc(String propertyName) {
		c.addOrder(Order.desc(propertyName));
		return this;
	}
	
	public Dao<T, K> withExample(T example) {
		c.add(Example.create(example).enableLike(MatchMode.ANYWHERE).ignoreCase());
		return this;
	}
	
	public Dao<T, K> withResults(int first, int max) {
		c.setFirstResult(first);
		c.setMaxResults(max);
		return this;
	}
	
	public int count() {
		c.setProjection(Projections.rowCount());
		return (Integer) c.uniqueResult();
	}
	
	@SuppressWarnings("unchecked")
	public List<T> all() {
		return c.list();
	}
	
	@SuppressWarnings("unchecked")
	public T one() {
		return (T) c.uniqueResult();
	}

}

Alguns exemplos de utilizacao:


List<User> allUsers = userDao.find().all();

List<User> activeUsers = userDao.find().withActiveStatus(true).all();

List<User> activeUsersSortedByName = userDao.find().withActiveStatus(true).withOrderByAsc("name").all();

List<User> veryVerySpecific = userDao.find().withActiveStatus(true).withOrderByAsc("name").withResults(0, 25).all();

List<Product> topTenExpensive = productDao.find().withOrderByDesc("price").withResults(0, 10).all();

Product foundByExample = productDao.find().withExample(product).one();

int activeGames = gameDao.find().withActiveStatus(true).count();

Sugestoes sao bem vindas…

Sds

wagner.montalvao

eu faço assim:

interface DAO:

[code]import java.util.List;

public interface BaseDAO {
/*
* Ações possíveis feita com CRUD. Os métodos cujos parâmetros
* são do tipo java.util.List, são enviados em massa, ou seja,
* os dados são processados e armazenados e apenas no final o
* aplicativo lança o commit. Para grande massa de dados é mais
* viável pois o tempo de processo é muito maior quando abre apenas
* uma conexão e aplica apenas uma ação de persistencia
* */
public T remove(T object) throws Exception;
public T persist(T object) throws Exception;
public T update(T object) throws Exception;

public List<T> update(List<T> object) throws Exception;
public List<T> persist(List<T> list) throws Exception;
public List<T> remove(List<T> object) throws Exception;
/*
 * Aqui se aplica todos os tipos de buscas possível para se fazer
 * dentro de uma aplicação padrão.
 * */
public List<T> findAll() throws Exception;

public T load(Object obj) throws Exception;

}[/code]

classe abstrata DAOImpl:

[code]import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;

import br.edu.unianhanguera.plt.dao.BaseDAO;

public abstract class BaseDAOImpl implements BaseDAO {
protected EntityManager em = null;

@Override
public abstract List<T> findAll() throws Exception;
@Override
public abstract T load(Object obj) throws Exception;

@Override
public T persist(T object) throws Exception {
	EntityTransaction transaction = em.getTransaction();
	transaction.begin();
	try{
		em.persist(object);
		transaction.commit();
		return object;	
	}catch(Exception e){
		transaction.rollback();
		throw e;
	}
}

@Override
public List<T> persist(List<T> list) throws Exception {
	EntityTransaction transaction = em.getTransaction();
	transaction.begin();
	try{
		for(T object:list)
			em.persist(object);
		transaction.commit();
		return list;	
	}catch(Exception e){
		transaction.rollback();
		throw e;
	}
}

@Override
public T remove(T object) throws Exception {
	EntityTransaction transaction = em.getTransaction();
	transaction.begin();
	try{
		em.remove(object);
		transaction.commit();
		return object;	
	}catch(Exception e){
		transaction.rollback();
		throw e;
	}
}

@Override
public List<T> remove(List<T> list) throws Exception {
	EntityTransaction transaction = em.getTransaction();
	transaction.begin();
	try{
		for(T object:list)
			em.remove(object);
		transaction.commit();
		return list;	
	}catch(Exception e){
		transaction.rollback();
		throw e;
	}
}

@Override
public T update(T object) throws Exception {
	EntityTransaction transaction = em.getTransaction();
	transaction.begin();
	try{
		em.merge(object);
		transaction.commit();
		return object;	
	}catch(Exception e){
		transaction.rollback();
		throw e;
	}
}

@Override
public List<T> update(List<T> list) throws Exception {
	EntityTransaction transaction = em.getTransaction();
	transaction.begin();
	try{
		for(T object:list)
			em.merge(object);
		transaction.commit();
		return list;	
	}catch(Exception e){
		transaction.rollback();
		throw e;
	}
}

}[/code]

quando eu preciso criar uma classe (Pessoa por exemplo) eu a interface DAO:

e o DAOImpl

que aí eu tenho todas as classes básicas já implementadas e na camada businesse eu trabalho apenas com a interface, geralmente setando elas com Spring

wagner, mas que coisa mais linda ficou, hein?

Engraçado como ficamos sempre presos a conceitos que aprendemos no passado. E é muito difícil sair desses conceitos. Eu sempre fiz repositories baseado em MyRepository.find, MyRepository.load, MyRepository.store, MyRepository.delete… (uso o termo repository ao invés de DAO, assunto já discutivo no guj)

No meu caso eu uso os repositories como local stateless session beans, e infelizmente parece que não funcionaria bem no meu caso, pois o atributo Criteria estaria em shared-mode, já que são stateless beans.

Abraços

Segue a minha abordagem sobre isso

Antes de ler, os métodos getDdi() e getDdd() aparecem de forma dinamicas e são oriundos da classe Telefone.class… qualquer refactory na classe Teleofne é relfetido na consulta, pois não uso strings. Sempre apos chamar uma propriedade do restrict todos os método da classe do Criterio aparecem.

infelismente não da pra fazer uma query continua, pois as propriedades do Telefone não tem como retornar para o criteira… mas fica de facil entendimento…

Esse meu criteria já tem boa parte dos métodos do hibernate


	Criteria&lt;Telefone&gt; query = Criteria
		.forClass(Telefone.class)
		.setFirstResult(0)
		.setMaxResults(20);
	
	Restriction&lt;Telefone&gt; restrict = query.addRestriction();
	
	restrict.isEquals(55).getDdi();
	restrict.isBetween(83, 85).getDdd();
	
	query.addOrder().ascIgnoreCaseBy().getNumero();
	
	//aqui vem a lisa dos telefones do ddi 55 com ddd entre 83 a 85
	List&lt;Telefone&gt; telefones = genericDao.list(query);
	
	//poderia ter usado genericDao.find(query); para encontra o primeiro