Duvidas sobre Dao + Hibernate + Repository

Olá, pessoal já li várias discussões sobre Repository X Dao aqui no GUJ, mas ainda assim algumas dúvidas persistem. Atualmente
trabalho com uma arquitetura definida assim:

Uma interface Dao

package br.com.oticaweb.persistence.dao;

public interface Dao<T> {
	
	public void save(T t);
	public void save_or_update(T t);
	public void update(T t);
	public void delete(T t);
	public T get(Serializable id);
	public T load(Serializable id);
	public List<T> findAll();
	public List<T> findAllByExample(T object);
	public List<T> findAllByExample(T object, String orderBy);
	public List<T> findAllByExampleWithPagination(T object,int min, int max);
	public T findByExample(T object);

A implementação com Hibernate

 class HibernateDao<T> implements Dao<T> {
	protected Session session;
	protected Class<T> persistenceClass;

	public HibernateDao(Session session, Class<T> persistenceClass) {
		super();
		this.session = session;
		this.persistenceClass = persistenceClass;
	}

	public void save(T t) {
		session.save(t);
	}

	public void save_or_update(T t) {
		session.saveOrUpdate(t);

	}

	public void delete(T t) {
		session.delete(t);
	}

	public void update(T t) {
		session.update(t);
	}

	@SuppressWarnings("unchecked")
	public T get(Serializable id) {
		return (T) session.get(persistenceClass, id);
	}

	@SuppressWarnings("unchecked")
	public T load(Serializable id) {
		return (T) session.load(persistenceClass, id);
	}
	
	
	@SuppressWarnings("unchecked")
	public List<T> findAll() {
		Criteria c = session.createCriteria(persistenceClass);
		return c.list();
	}


	

	protected Integer countByCriterion(Criterion criterion) {
		Criteria c = session.createCriteria(persistenceClass);
		c.add(criterion);
		c.setProjection(Projections.rowCount());
		return (Integer) c.uniqueResult();
	}


	@SuppressWarnings("unchecked")
	public List<T> findAllByExample(T object) {
		Example example = Example.create(object).ignoreCase().enableLike(
				MatchMode.ANYWHERE);
		Criteria c = session.createCriteria(persistenceClass);
		c.add(example);
		return c.list();
	}

	@SuppressWarnings("unchecked")
	public List<T> findAllByExample(T object, String orderBy) {
		Example example = Example.create(object)
					.ignoreCase()
					.enableLike(MatchMode.ANYWHERE);
		Criteria c = session.createCriteria(persistenceClass);
		c.add(example);
		c.addOrder(Order.asc(orderBy));
		return c.list();
	}

	@SuppressWarnings("unchecked")
	public List<T> findAllByExampleWithPagination(T object, int min, int max) {
		Integer count = 0;
	
		Example example = Example.create(object)
		.ignoreCase()
		.enableLike(MatchMode.ANYWHERE);
		Criteria c = session.createCriteria(persistenceClass);
		c.add(example);
		
		count = countByCriterion(example);

		if (count < max)
			max = count;

		if (min >= 0)
			c.setFirstResult(min);
		if (max >= 0)
			c.setMaxResults(max);


		return c.list();
		
	}

	@SuppressWarnings("unchecked")
	public T findByExample(T object) {
		Example example = Example.create(object)
		.ignoreCase();
		Criteria c = session.createCriteria(persistenceClass);
		c.add(example);
		return (T) c.uniqueResult();
	}

}

Ai quando quero algum metodo novo crio uma interface tipo:

public interface FuncionarioDao extends Dao<Funcionario> {
	public List<Funcionario> findAllByNameAndId(String name, Serializable id, int min,int max)throws NoResultException;
}

E implemento com um FuncionarioDaoImpl assim:

 FuncionarioDaoImpl extends HibernateDao<Funcionario> implements FuncionarioDao{
@SuppressWarnings("unchecked")
	public List<Funcionario> findAllByName(String name, int min, int max) throws NoResultException {
		Integer count = 0;
	
		Criteria c=session.createCriteria(Funcionario.class);
                 Conjunction conjunction=Restrictions.conjunction().add(Restrictions.eq("id", "id")).add(Restrictions.ilike("nome", name,MatchMode.ANYWHERE));

		c.add(conjunction);
		count = countByCriterion(conjunction);

		if (count < max)
			max = count;

		if (min >= 0)
			c.setFirstResult(min);
		if (max >= 0)
			c.setMaxResults(max);

		return c.list();
}

Ai disponibilizo uma Factory para criação dos Daos onde injeto a Session assim:

public class DaoFactory {
	private final Session session;
	
	public DaoFactory() {
		session=HibernateUtil.currentSession();
	}

	public FuncionarioDao getFuncionarioDao(){
		return new FuncionarioDaoImpl(session,Funcionario.class);
	}


	public FilialDao getFilialDao() {
		return new FilialDaoImpl(session,Filial.class);
	}
	
	public Dao<Secao> getSecaoDao(){
		return new HibernateDao<Secao>(session,Secao.class);
	}
}

Agora minhas dúvidas :

Acabando achando desnecessário interface Dao, pois se por exemplo se amanha quiser mudar a camada de Persistencia para JPa por exemplo, ficaria dificl implentar metodos como findByExample,por isso acho minha interface acoplada demais ao Hibernate.

Então qual seria a desvantagem de não ter interfaces trabalhando direto com a implementação?

Outra dúvida, esse modelo acima, está mais para um Repository ou Dao, muito genta fala que o Repository e uma interface com Semântica melhor do que o Dao então neste caso poderia ter uma interface assim:

interface Repository<T> 
public void add(T t);
public void remove(T t);

O problema aqui é, onde entraria os metodos findAllByExample, no Repository ou nos Dao’s?

Como podem ver estou um pouco confuso com estes dos Patterns.

Se alguem puder esclarecer agradeço.

Rapaz, eu uso generic dao e não preciso estar colocando uma interface para cada dão não!

Uso só o daoFactory, o Dao e uma classe específica… Exemplo UsuarioDao

Olá moska obrigado pela resposta.

Entao você tem

Interface Dao
HibernateDao implments Dao

e tipo

UsuarioDao extends HibernateDao{

//metodo diferentes
}

Isso?

Vou exemplificar aqui com uma classe simples.

Classe de negócio

/**
 * Recurso a ser acessado por um usuário em um perfil
 * @author Igor Cavalcante
 */
@Entity
public class Recurso extends Domain implements Serializable, Treeable {

	private static final long serialVersionUID = -5049948424198784301L;
	
	@Id
	private Long id;
...
}

Agora meu daofactory que chama os respectivos daos e implementa métodos de conexão.

public class DaoFactory {

...

    public PerfilDao getPerfilDao() {
        return new PerfilDao(this.session);
    }

...

}

Agora o dao, que implementa a maioria das tarefas comuns de bancos de dados.

public class Dao<T> {

    protected final Session session;
    @SuppressWarnings("unchecked")
    private final Class classe;

    @SuppressWarnings("unchecked")
    public Dao(Session session, Class classe) {
        this.session = session;
        this.classe = classe;
    }

    public void add(T obj) {
        this.session.save(obj);
        this.session.flush();
    }

    public Serializable save(T obj) {
        Serializable r = this.session.save(obj);
        this.session.flush();
        return r;
    }

    public void remove(T obj) {
        this.session.delete(obj);
    }

    public void update(T obj) {
        this.session.merge(obj);
        this.session.flush();
    }

    @SuppressWarnings("unchecked")
    public List<T> listAll() {
        return this.session.createCriteria(classe).list();
    }

    @SuppressWarnings("unchecked")
    public T getById(Long id) {
        return (T) session.get(classe, id);
    }

    @SuppressWarnings("unchecked")
    public List<T> findByExample(T obj) {
        Criteria c = this.session.createCriteria(classe);
        c.add(Example.create(obj).enableLike());
        return c.list();
    }

    @SuppressWarnings("unchecked")
    public T getByExample(T obj) {
        Criteria c = this.session.createCriteria(classe);
        c.add(Example.create(obj).enableLike());
        return (c.list().size() > 0 ? (T) c.list().toArray()[0] : null);
    }
}

Agora um dao específico de uma classe. Que extende Dao e eu só preciso colocar os métodos que não foram implementados na classe Dao.
Mesmo que não haja nenhum método para ser implementado, eu vou precisar desta classe, para o sistema saber qual a entidade que eu estou usando através de generics.

package br.edu.uncisal.almoxarifado.dao;

import org.hibernate.Session;

import br.edu.uncisal.almoxarifado.model.Perfil;

public class PerfilDao extends Dao<Perfil> {

    PerfilDao(Session session) {
        super(session, Perfil.class);
    }
    
}

Qualquer coisa, tamo por aí.

Não faz muito sentido para a maioria dos casos se ter um DAO quando se está trabalhando com um framework ORM como JPA/Hibernate.

Isso acaba tornando-se um anti-pattern,
http://www.rponte.com.br/2009/06/08/no-more-daos/

Isso é questionável…

DAO, Repository, whatever… você precisa de alguma coisa para não expôr o mecanismo de persistência para o resto da sua aplicação.

Outra coisa questionavel é o findByIsso findByAquilo. Existem algumas formas de se evitar isso, a mais simples com um map propriedade, valor de busca, que torna a interface do dao bem mais simples.

Interfaces normalmente parecem desnecessarias, mas normalmente somos nos que estamos pensando errado. Elas desacoplam completamente as classes clientes das implementacoes, se mesmo com o uso de interfaces elas continuam acopladas é porque estao sendo mal usadas.

Na verdade o que esta sobrando ai na sua aplicacao nao sao as interfaces, mas a factory dos daos. Use Spring e chute essa factory pra longe.

[quote=ravisantos]Como podem ver estou um pouco confuso com estes dos Patterns.
[/quote]

Super normal, são confusos mesmo. Você já tentou ler a bibliografia recomendada?

Olá, Philip e qual bibliografia seria recomendada?

Valeu

[quote=YvGa]Outra coisa questionavel é o findByIsso findByAquilo. Existem algumas formas de se evitar isso, a mais simples com um map propriedade, valor de busca, que torna a interface do dao bem mais simples.

Interfaces normalmente parecem desnecessarias, mas normalmente somos nos que estamos pensando errado. Elas desacoplam completamente as classes clientes das implementacoes, se mesmo com o uso de interfaces elas continuam acopladas é porque estao sendo mal usadas.

Na verdade o que esta sobrando ai na sua aplicacao nao sao as interfaces, mas a factory dos daos. Use Spring e chute essa factory pra longe.[/quote]

Olá, na verdade o findByIsso, findByAquilo, eu resolve com o findByExample do hibernate, onde passo objeto populado com os atributos que quero q ele busque. Desta forma, somente se for uma pesquisa bem específica, como por exemplo misturar um like, com um eq, junto com um group by, é que crio um metodo difirente no XXXDAo especifico;

[quote=ravisantos]Olá, Philip e qual bibliografia seria recomendada?
[/quote]

Oi,

Por mim, esta: http://blog.fragmental.com.br/2008/05/20/trilha-de-livros-desenvolvedor/

[quote=pcalcado][quote=ravisantos]Olá, Philip e qual bibliografia seria recomendada?
[/quote]

Oi,

Por mim, esta: http://blog.fragmental.com.br/2008/05/20/trilha-de-livros-desenvolvedor/

[/quote]

Olá Philip, obrigado pela lista. Excelentes livros, porventura atualmente estou lendo o Fundamentals of object-oriented design in UML, e estou gostando muito.

Mas pelo que li em outros lugares, um repositorio, só faria sentido caso estivesse usando DDD, ou pelo menos um Domain Model, tentando “blindar” a cama de negócios, do contrário, acho que o Dao se encaixa melhor.

[quote=pcalcado][quote=ravisantos]Olá, Philip e qual bibliografia seria recomendada?
[/quote]

Oi,

Por mim, esta: http://blog.fragmental.com.br/2008/05/20/trilha-de-livros-desenvolvedor/

[/quote]

Philip, muitos questionam o uso do Repository com o Dao ou seja o Repositorio sendo um interface para o Dao. Dizem que o dao neste caso nao faz sentido bastando ter por composicao dentro do repositorio, uma Session ou EntityManager.

O que você pensa desta abordagem?

[quote=ravisantos]
Philip, muitos questionam o uso do Repository com o Dao ou seja o Repositorio sendo um interface para o Dao. Dizem que o dao neste caso nao faz sentido bastando ter por composicao dentro do repositorio, uma Session ou EntityManager.

O que você pensa desta abordagem?[/quote]

Desde que não haja dependência nenhuma da as classes da Camada de Negócio para Hibernate/JPA/etc. Não há qualquer problema. O que geralmente eu tenho hoje em dia é um UsuarioRepositorio como interface que é implementado por um HibernateUsuarioRepositorio. O primeiro é da Camada de Negócios e o último da Persistência.

Eu discordo.
Não necessariamente um DAO, mas acho válido uma camada que abstraia a persistência das minhas entidades de negócio.(DAO, Repository, DataMapper, etc)

[quote=pcalcado][quote=ravisantos]
Philip, muitos questionam o uso do Repository com o Dao ou seja o Repositorio sendo um interface para o Dao. Dizem que o dao neste caso nao faz sentido bastando ter por composicao dentro do repositorio, uma Session ou EntityManager.

O que você pensa desta abordagem?[/quote]

Desde que não haja dependência nenhuma da as classes da Camada de Negócio para Hibernate/JPA/etc. Não há qualquer problema. O que geralmente eu tenho hoje em dia é um UsuarioRepositorio como interface que é implementado por um HibernateUsuarioRepositorio. O primeiro é da Camada de Negócios e o último da Persistência.[/quote]

Olá Philip obrigado pela resposta. Então mas quando se usa a session direto dentro do Repository, você acaba criando uma dependência com o Hibernate.
Na verdade acredito que quando usa o Hibernate, ele se “interfere” demais no seu modelo, principalmente se usa Annotations, por isso acredito que abstrair tanto nao faria sentido.
Exemplo digamo que eu tenho um metodo:

findByExample em uma interface Dao, seria muito dificil implementar o mesmo exemplo com JDBC puro, por isso acho que interfaces são desnecessárias.
O que geralmente tenho e o Repositorio como classe, ou seja o meu modelo já está dependente do Hibernate, mesmo se eu tivesse uma interface REpositorio e uma classe Repositorio, acho que seria dificl modificar a camada de Persistencia sem precisar alterar muita coisa no meu modelo.

Neste caso que voce cololocou em especifico, voce teria um modelo parecido com isso:

RepositorioInterface -> RepositorioClasse -> Session ou EntityManager -> SGDB
ou voce ainda teria o DAo para promover alguma outra abstracação

RepositorioInterface -> RepositorioClasse -> Dao -> Session ou EntityManager -> SGDB

?

Valeu

Vamos lá (de novo)

Repositorio : objeto responsável por encontrar instancias de entidades. Se ele é responsável por encontrar , os métodos find ficam com ele. Obvio, não ?
DAO : objecto responsável por acessar dados em fontes de tecnologia diversa. Ele isola a tecnologia de persistencia. Normalmente é um por entidade. (DAO genérico é uma aberração da natureza).
DomainStore : objecto responsável por toda a persistencia do dominio (o nome diz tudo). Ele utiliza DAO por baixo dos panos mas o cliente não os vê.
É um domainstore por dominio.

Um repositorio pode usar DAO, mas é mais natural utilizar DomainStore porque ambos se relacionam ao dominio.

Hibernate : implementação “comercial” de um DomainStore especializado em persistencia em banco de dados.

Se vc usa o hibernate você está dizendo que a persistencia apenas pode acontecer em banco de dados ( em traços largos, porque em tese o hibernate é expansivel a outras midias… mas não existe isso ainda).

O Repositorio tem aquele interface que vc chama de DAO. Com add, remove, e os finds. O repositorio base tem o findByID e os findAll() que são mais utilizados.

Um repositorio especifico para Cliente, por exemplo tem mais métodos find especificos de Cliente. Por exemplo findBySellRegion(Region r), findByNetRetail(Money minRetail) , etc … um report, por exemplo, é um método find do respositorio.

É facil implementar estes métodos ? Não. Vc precisa usar truques como Specification para a paginação e FastLaneReader para a leitura rápida sem consumir muitos recursos. Repositoriy é a melhor forma de modelar acesso a dados do ponto de vista do core de negocio. ( O padrão repositorio é velho. O Fowler que inventou antes do DDD existir. Repositorio não implica em DDD).

Bom, mas como implementar isso ?

Vamos ver com hibernate primeiro.
Para cada método vc delega ao hibernate de forma semelhantes ou qe já tem. Vc pode ter um HibernateAbstractRepository que implementa o básico do add , remove , etc… que é sempre igual. Além disso vc precisa de um método protegido findByCriteria(Criteria c). Este método é o coração da coisa toda. Um ClienteRepository irá implementar os seus finds especificos criando criterios do hibernate e mandar para aquele método. Isso é simples e é facil acrescentar novos métodos find. Se vc precisar de paginação , fastlane ou outras coisas, vc precisa incrementar o findByCriteria para ser o suficientemente poderoso para todos esses cenários. Um bom padrão para isso é criar uma interface padrão para a resposta ( que não seja um list). eu costumo usar algo como

public interface QueryResult<T> {
   public T first();
   public Collection<T> all()
   public boolean isEmpty();
   public int count();
   public QueryResult subQuery(int first, int count);

}

Esta interface permite criar um relay entre a chamada ao método find do repositorio e a real execução da pesquisa. Permite que a pesquisa seja otimizada , pois um count() não precisa ser all().size() , pode ser um SELECT count FROM … e o subQuery é para permitir paginação sem ter que colocar isso no contrato do método find. Assim vc pode paginar qualquer resultado de qualquer método find.
moral da historia, um bom design OO ajuda muito.

Bom, blz. Vc tem o seus repositorios utilizando o hibernate. Mas e se quiser se livrar do hibernate e trocar por JPA (supondo que isso é possivel)?
Ai vc mantém o repositorio criandos critérios, mas agora, vc tem que implementar a API de critérios de forma independente do Hibernate e do JPA ou de qq outra coisa. Depois vc cria um RepositoryStrategy que contém as implementações reais de add, remove, findByCriteria. Todos os métodos do repositorio delegam à estratégia. E a estratégia delega ao mecanismo real do hibernate, jpa , etc… A estratégia é injetada no repistorio via construtor.

A estratégia irá traduzir o seu critério de pesquisa agnostico (que não depende da tecnologia subjacente) para um critério especifico ( do hibernate, do jpa ,etc…). A estratégia irá executar o mecanismo especifico e retornar os dados. É assim que vc isola a tecnologia de persistencia do repositorio em si.

Para o caso simples e cotidiano pode manter o hibernate atrelado ao repositorio e só abstrair o hibernate quando outro mecanismo aparecer/for necessário.

E o DAO ? O DAO vc esquece.

+1 Esquece o DAO.

O objetivo de um framework como Hibernate, assim como um Repositório, é dar a sensação de que os objetos estão na memória, e portanto não preicsam ser acessados (por um DAO no caso). Claro que isso é uma sensação provocada pelo mecanismo de persistencia, vc sabe… utilizar um DAO quebra esse encanto. A diferença do repositório é que na construção de domínio não é permitido que o model tenha qualquer dependencia com infraestrutura. Mas numa aplicação normal não machuca usar o Session do hibernate como “repositorio”.

Eu discordo.
Não necessariamente um DAO, mas acho válido uma camada que abstraia a persistência das minhas entidades de negócio.(DAO, Repository, DataMapper, etc)[/quote]
Por isso eu disse “para a maioria dos casos”. Se você precisa de algo mais abstrato e que faça mais sentido ao teu modelo então use-o, nem que seja um método que revele a devida intenção para seu modelo.

Agora não é interessante fazer da exceção a regra.

Rafael, Phillip e Sérgio, você que são todos da onda “não use DAO, DAO faz mal”, por favor:

  1. Coloquem o código de um dos repositórios que vocês codificaram no projeto atual que vocês acham que esta corretamente implementado.

  2. Imagine que você tenha uma tela de filtro comum, com N campos e um botão “Buscar”. Coloque o(s) método(s) do seu repositório que faz a busca. Se você não usa repositório, coloque o código do que quer que você use.

[]'s

Rubem