Duvidas sobre Dao + Hibernate + Repository

46 respostas
R

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.

46 Respostas

M

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

R

Olá moska obrigado pela resposta.

Entao você tem

Interface Dao
HibernateDao implments Dao

e tipo

UsuarioDao extends HibernateDao{

//metodo diferentes
}

Isso?

M

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

R

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/

R

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.

Y

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.

P

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

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

R

Olá, Philip e qual bibliografia seria recomendada?

Valeu

R

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.

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;

P

ravisantos:
Olá, Philip e qual bibliografia seria recomendada?

Oi,

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

R

pcalcado:
ravisantos:
Olá, Philip e qual bibliografia seria recomendada?

Oi,

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

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.

R

pcalcado:
ravisantos:
Olá, Philip e qual bibliografia seria recomendada?

Oi,

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

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?

P

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?

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.

R

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)

R

pcalcado:
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?

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.

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

S

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.

M

+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”.

R

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

R

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

R

rponte:
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.

A regra, utilizando somente Hibernate(mas que JPA seria bem próximo a isso), seria algo do tipo:

class Pessoa{

public void inserir(Pessoa p){
   try{
   createSession();
   beginTransaction();
   session.save(p);
   tx.commit();
   session.close();
   }.....

Essa é a sua sugestão? Ou entendi errado?

M
class ColecaoDePessoas {

public void inserir(Pessoa p){
   try{
   createSession();
   beginTransaction();
   session.save(p);
   tx.commit();
   session.close();
   }.....

Pra que criar uma camada se você pode criar um objeto?

R
mochuara:
class ColecaoDePessoas {

public void inserir(Pessoa p){
   try{
   createSession();
   beginTransaction();
   session.save(p);
   tx.commit();
   session.close();
   }.....

Pra que criar uma camada se você pode criar um objeto?

Então a questão é só nomenclatura? Colocar o sufixo DAO caracteriza um anti-pattern?

public class PessoaDAO{
 public void inserir...
M

Sim, porque ColecaoDePessoas não é um DAO. Ele não lida com “data sources” mas com objetos “em memoria”.

R

A regra, utilizando somente Hibernate(mas que JPA seria bem próximo a isso), seria algo do tipo:

class Pessoa{

public void inserir(Pessoa p){
   try{
   createSession();
   beginTransaction();
   session.save(p);
   tx.commit();
   session.close();
   }.....

Essa é a sua sugestão? Ou entendi errado?

Levando em consideração que sua classe Pessoa faz parte da sua Camada de Aplicação, sim, seria algo do tipo. Evidentemente que sem este “glue code” todo para controle transacional.

R

sergiotaborda:
ravisantos:

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.

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.

Olá Sérgio, depois dessa aula. Algumas duvidas ainda me restaram. Por exemplo este metodo findByCriteria retornaria o que ?

E essa interface quema implementaria, me interessei por essa interface mais ainda não sei como aplica-la na pratica. Sem querer pedir demais poderia dar um exemplo;

Obrigado!

S

Sim. Representa que você não soube atribuir corretamente a responsabilidade à classe.
É a mesma coisa que vc ter uma lista e chamar Pessoa.

Para o caso do DAO isso é comum hoje em dia. Eu chamo isso de Sindrome de DAO

S

O que vc acha que deve. O pessoal gosta de retornar List, mas eu acho melhor retorar algo como o QueryResult que demonstrei.
Como ele é muito simples aplicar o padrão FastLane Reader .

Suponho que esteja falando do QueryResult. Quem a implementa é a camada mais inferior capaz de entender T. Normalmente o Repositorio ou o DomainStore. Quando mais baixa a camada mais otimizada a leitura e mais rápido o sistema. Se poder ser implementada pelo cada que lê do banco tanto melhor. Normalmente com Hibernate este objecto é implementado pelo Repositorio ou por uma estratégia do Repositorio.

Lembre-se que o Repositorio não está origado a usar persistencia. Basta que ele use preservação de dados. Se os dados são fixos estes requisitos são dados de barato. Um exemplo é um RoleRepository que mantem as entidades Role que representam as permissões dos usuários. normalmente Vc carrega um Map < id , Role > com todos os roles. Ai vc tem finds. Como tudo está na memoria, a otimização não é muita e vc acaba utilizando uma implementação baseada em colleções para o QueryResult.

public class CollectionQueryResult<T> implements QueryResult<T> {

           public CollectionQueryResult(Collection<? extends T> items ){
                    this.items = items;
           }

           public T first( ){
                 return this.items.isEmpty() ? null : this.items.iterator().next();
          }

           public Collection<T> all(){
                 return Collections.unmodifiableCollection(this.itens);
          }

          public boolean isEmpty(){
              return this.items.isEmpty() ;
          }

          public long count(){
                return this.items.size();
          }
}

Em uma situação de Fastalane Reader os métodos invocam as procuras directamente.
Por exemplo em JDBC puro o count invoca um SELECT Count, o empty retorna this.count() == 0 , o fisrt() retornar um Select * TOP 1 ( ou equivalente) e o all retorna um SELECT *

Esse é o poder do QueryResult. Ele pode ser implementado de trilhentas formas, mas para quem usa o repositorio e invoca, por exemplo:

QueryResult<XPTO> osBons =  repositorioXPTO.findAllActive();

for ( XPTO x : osBons.all() ){

     System.out.println(x.toString());
}

Isso é totalmente encapsulado. Mas mesmo assim é eficiente.

R

sergiotaborda:
Sim. Representa que você não soube atribuir corretamente a responsabilidade à classe.
É a mesma coisa que vc ter uma lista e chamar Pessoa.

Discordo.
Pra mim é pura e simples questão de nomenclatura. A responsabilidade do objeto continua exatamente o mesmo, antes de DDD e alguns outros padrões utilizavamos DAO para isso, agora mudamos o nome para fazer a mesma coisa.

R

Rafael Nunes:
sergiotaborda:
Sim. Representa que você não soube atribuir corretamente a responsabilidade à classe.
É a mesma coisa que vc ter uma lista e chamar Pessoa.

Discordo.
Pra mim é pura e simples questão de nomenclatura. A responsabilidade do objeto continua exatamente o mesmo, antes de DDD e alguns outros padrões utilizavamos DAO para isso, agora mudamos o nome para fazer a mesma coisa.

+1

E ainda estou esperando os exemplos… alguém tem algum exemplo de um repositório bem implementado?

R

Rafael Nunes:
sergiotaborda:
Sim. Representa que você não soube atribuir corretamente a responsabilidade à classe.
É a mesma coisa que vc ter uma lista e chamar Pessoa.

Discordo.
Pra mim é pura e simples questão de nomenclatura. A responsabilidade do objeto continua exatamente o mesmo, antes de DDD e alguns outros padrões utilizavamos DAO para isso, agora mudamos o nome para fazer a mesma coisa.

Rafael,

Nomes são importantes, não entendo porque você trata como algo irrelevante. Além de que DAO e Repository são conceitos distintos, isso já foi muito discutido aqui mesmo no GUJ.

Quanto a DAO ser um anti-pattern está ligado a sua utilização incorreta ao tentar abstratir um framework ORM como JPA/Hibernate. Não há esta necessidade para muitos casos, pois o JPA/Hibernate além de outras coisas atua como seu DAO.

R

Sim, um DAO pode ser uma implementação de um Repository. Não acho que sejam irrelevantes, mas não acredito que utilizar uma determinada nomenclatura torna a solução errada, se a responsabilidade é exatamente a mesma.

rponte:

Quanto a DAO ser um anti-pattern está ligado a sua utilização incorreta ao tentar abstratir um framework ORM como JPA/Hibernate. Não há esta necessidade para muitos casos, pois o JPA/Hibernate além de outras coisas atua como seu DAO.

Essa é a parte principal que eu discordo, não vejo sentido em acoplar/tornar dependente minhas entidades/POJOs do mecanismo de persistência ou qualquer outro framework. Ao menos nunca passei em um cenário em que isso faria sentido.

R

Rafael Nunes:

rponte:

Quanto a DAO ser um anti-pattern está ligado a sua utilização incorreta ao tentar abstratir um framework ORM como JPA/Hibernate. Não há esta necessidade para muitos casos, pois o JPA/Hibernate além de outras coisas atua como seu DAO.

Essa é a parte principal que eu discordo, não vejo sentido em acoplar/tornar dependente minhas entidades/POJOs do mecanismo de persistência ou qualquer outro framework. Ao menos nunca passei em um cenário em que isso faria sentido.

Você sinceramente acha que tuas entidades e a forma como trabalha não estão ligadas/dependentes ao JPA/Hibernate?

R

De certa forma sim, mas não vejo sentido em criar mais dependência/acoplamento.

Se eu partir deste princípio, qual exatamente a diferença em eu transforma também esta entidade em um Managed Bean?

M

De certa forma sim, mas não vejo sentido em criar mais dependência/acoplamento.

Se eu partir deste princípio, qual exatamente a diferença em eu transforma também esta entidade em um Managed Bean?

Você introduziu uma camada de indirecao, logo mais dependências.

Rafael Nunes:

Se eu partir deste princípio, qual exatamente a diferença em eu transforma também esta entidade em um Managed Bean?

Do ponto de vista dele ser “Managed” nenhum. Ambos não possuem dependência com quem os gerencia. Aliás se suas entidades são dependentes de algum framework ORM você está fazendo errado. No exemplo anterior ColeçãoDePessoas não se encaixa na minha definição de entidade.

J

Não vejo problema nenhum em utilizar um DAO que utiliza hibernate na camada de persistência implementando o Repository (que fica no domain). Aliás, é uma implementação que utilizo frequentemente. Assim o mecanismo de persistencia (DAO) fica separado do domain, que encherga apenas o repository.

Essa “fobia” da palavra DAO não faz o menor sentido pra mim. E usar session (ou entitymanager, no caso de jpa) direto (sem um repository) na minha opinião é uma péssima idéia.

R

Não possui até eu precisar inserir um SelectItem, DataModel, etc, na entidade.

Esse foi o ponto que eu discordei do rpontes, não ter essa camada(que você deu um exemplo chamando de Colecao e eu chamando de DAO) que abstraia isso da minha entidade.

S

Isso é uma mentira tão grande e tão obvia que é impossivel que vc acreditasse nisso se vc entendesse o que está dizendo.
A todos vcs que - ainda - acham que DAO e Repositorio é a mesma coisas só tenho uma coisa a dizer : aprendam o que signfica Separação de responsabilidade.

Alguns pontos para começar:

  1. Se A é B , a reponsabilidade de A é , no minimo, a mesma de B.
  2. Se A implementa I , a responsabilidade de A é também a de I.

Se o DAO implementa o repositorio e o repositorio é um objeto do dominio, e como tal, tem responsabilidades do dominio, então o DAO também teria. Isso é um contrasenso porque o DAO não é um objeto do dominio!

Se isto não é claro como a água, leia de novo. Leia mais sobre responsabilidade e o Principio de Separação de Responsabilidade.
tá meio fraco…

O rei vai nu e só vcs não enxergam.

J

Isso é uma mentira tão grande e tão obvia que é impossivel que vc acreditasse nisso se vc entendesse o que está dizendo.
A todos vcs que - ainda - acham que DAO e Repositorio é a mesma coisas só tenho uma coisa a dizer : aprendam o que signfica Separação de responsabilidade.

Alguns pontos para começar:

  1. Se A é B , a reponsabilidade de A é , no minimo, a mesma de B.
  2. Se A implementa I , a responsabilidade de A é também a de I.

Se o DAO implementa o repositorio e o repositorio é um objeto do dominio, e como tal, tem responsabilidades do dominio, então o DAO também teria. Isso é um contrasenso porque o DAO não é um objeto do dominio!

Se isto não é claro como a água, leia de novo. Leia mais sobre responsabilidade e o Principio de Separação de Responsabilidade.
tá meio fraco…

O rei vai nu e só vcs não enxergam.


Primeiro: quando eu disse que DAO e Repository são a mesma coisa?? Nunca falei nada nem próximo disso.

Eu tenho na minha camada de domínio o repositório (interface). O resto do domínio conhece apenas o repositório, não faz idéia se ele é implementado usando hibernate, jpa, jdbc ou o que for. A única dependência, por exemplo, de um AlunoService é com o AlunoRepository. Tenho um AlunoDAO que implementa AlunoRepository e é injetado no service. Poderia chamar esta classe de HibernateAlunoRepository ou seja la o que for, ela é apenas uma implementação do repositório.

Outra coisa: não acho que você tem o mínimo de respeito pra discutir com alguém que nem conhece desta maneira. A partir deste posts, teu posts tão ignorados. Abraço.

R

Sergio, se você ler o capitulo sobre repository do livro DDD do Eric Evans, vai ver que Repository é basicamente para encapsular busca de dados:

Mas pra frente ele fala que é como uma coleção que reune todos os objetos do sistema, mas podemos dizer que na prática da na mesma… Pelo que o livro descreveu, bate com o propósito do DAO:

Não é exatamente a mesma coisa, mas tem basicamente a mesma responsabilidade. Isso não da para discutir…

Se você pegar os exemplo dos livro sobre repository, são muito parecidos com os exemplos de DAO que se vê por aí…

S

@Juk

Você sim disse que é a mesma coisa.

Se A implementa B e nada mais, então A é B.
Isto é OO 101.

@Rubem Azenha

O mundo é maior que DDD. Leia sobre o padrão Repository no livro do Fowler e confronte com o padrão DataMapper (DAO) do mesmo livro. Não tem nada a haver com DDD.
O padrão Repository não pertence ao DDD e o nome “Repositorio” em DDD não diz respeito directamente ao padrão “Repository”.

Em DDD um repositorio pode ser qualquer coisa: um list, um DAO, um Map, um EJB, etc…
Em OO, o padrão Repository não pode ser um DAO ou um Map ou um List oum EJB. Ele pode ser composto por um DAO, um map, um list , um EJB…

São coisas totalmente diferentes. Quando se fala em DAO vs Repositorio não estamos em DDD porque em DDD essa diferença é irrelevante.
Quando falamos em DAO vs Repositorio estamos falando de OO e Padrões de Projeto. Estamos falando do Principio de Separação de Responsabilidade.
É um mundo maior que o de DDD.

Então o ponto é : Um objecto construido sob o padrão DAO tem a mesma responsabilidade que um construido sob Reposiotry ?
A resposta é não. Porquê ?

  1. Repositorio tem a responsabilidade primeira de conhecer o dominio. O DAO não.
  2. O DAO tem que conhecer a tecnologia de persistencia. O repositorio não.
  3. O repositorio executa pesquisas próprias ao domino onde os parametros são objetos que representam valores para parametros da pesquisa e nunca a pesquisa em si ( não QueryObjects) . O DAO só executa QueryObjects mas não os monta ( O DAO executa SQL, mas ele não escolhe o SQL)
  4. A implementação de um repositorio contém regras de negocio. A forma como as pesquisas são definidas é um regra. O DAO não contém regras de negocio. Ele executa de forma “burra” um certo comando.
  5. Mais geralmente DAO é um tipo especifico do padrão Service. Repositorio não é um Service.

Um exemplo simples da diferença. nos antigamentes um EJB tinha um home e vc executavas coias como findByXYZ() em cima do Home. A resposta era um objecto do tipo do EJB-Entidade ou uma lista deles. Este home tinha o papel de um repositorio do ponto de vista do resto do core de negocio. Mas era comum vc implementar os EJB-Entity usando um DAO para JDBC. Isto porque era suposto que seria possivel mudar o DAO ( por exemplo, se mudasse de banco). Os AS rápidamente viram que isto era muito chato e esta parte foi automatizada com o CMP. O DAO morreu, mas o Home(o repositorio) não. então :

  1. O reposiotrio nunca morre enquanto o dominio existe. o DAO morre quando a tecnologia de preservação (persistencia ou prevalencia) muda.
    Um DAO é plugável e intercambável. Ou seja, é suposto vc poder titar um JDBCDAO e substituir por um XMLDAO ou um LDAPDAO. Um repositorio não é substituivel. O repositorio não é substituivel pela mesma razão que um entity não é. Não faz sentido vc ter duas implemetações de cliente, ou produto. A unica coisa que faz sentido neste tipo de objeto é o uso do padrão Strategy. JDBCDAO , XMLDAO, etc… não são Strategy, são implementações per-se de DAO ( porque estamos usando o padrão Service no fim de contas).

É claro agora ?

R

Hum… realmente, se formos pegar a descrição de Repository do Fowler tem outro sentido. Eu estava me referindo a descrição do Eric Evans no seu livro sobre DDD.

Note só que em momento algum ele inviabiliza o uso do DAO\DataMapper e diz que o correto pra usar direto o mecanismo do persistência nas classes de negócio e que no caso, Repository é simplesmente uma camada entre a camada de persistência e a camada de negócios.

S

Rubem Azenha:
Hum… realmente, se formos pegar a descrição de Repository do Fowler tem outro sentido. (…)

Note só que em momento algum ele inviabiliza o uso do DAO\DataMapper e diz que o correto pra usar direto o mecanismo do persistência nas classes de negócio e que no caso, Repository é simplesmente uma camada entre a camada de persistência e a camada de negócios.

Eu alguma vez disse que DAO era inviável ? ou alguma vez disse que o correto é usar directo o mecanismo de persistencia ? Não, pois não ?

Agora que vc entendeu o que é um Repository vamos à segunda parte: porque eu não preciso mais do DAO ?

Antigamente o DAO era usada na arquitetura EJB <= 2 para acessar banco de dados. Este mecanismo realmente teve uma utilidade naquele tempo , mas nunca deslanchou (ninguem implementava daos para mais do que um banco). Os fabricantes de AS, sobretudo o JBoss tiveram a ideia de encapsular esta responsabilidade de uma vez por todas e nasceu o CMP ( Container Managed Persistence). Isto é o incio do fim para o DAO.

O CMP virou o hibernate e o hibernate virou uma API separada. O DAO ainda continua lá, mas por baixo de 7 camadas de tinta.

O Hibernate foi um sucesso e o modelo EJB <=2 foi bombardeado de todos os lados. O odio de xml , exceções mirabolantes, falta de injeção automática de dependencia , etc… levaram ao repensamento do modelo EJB ( um dos melhores modelos já construidos, mas cujas implementações tiveram que se antenar com os tempos)

Ora ai nasceram duas coisas: a visão de que era possivel manipular entidades de uma forma menos intrusiva como o hibernate fazia e que isso estava virando modelo ( o JDO outro candidato a substituir o CMP está ai).
Os JEE Core patterns foram revistos. Muita influencia de padrões como os do Fowler e outros trouxeram nova estrutura. Lembrando que as versões anteriores do EJB foram criada pela SUn mas a EJB 3 foi criada por todos.
O padrão que surgiu para matar o CMP e o BMP foi o DomainStore do qual o hibernate é uma implementação.
Sendo que a sun não poderia oficialmente suportar o modelo do hibernate nasceu a JPA. A JPA é para o DomainStore o que o JDBC é para o DataMapper. Com o mecanismo de providers é possivel ter o hibernate sem o problema de o suportar oficialmente. A JPA não é perfeita. só suporta SQL, mas é um começo. O ponto importante aqui é o DomainStore.

Este novo padrão é complexo. É na realidade um conjunto de padrões orquestrados por baixo de uma API publica simples. A palavra “Domain” aqui tb não tem nada a haver com o DDD. Claro que o “domain” se refere ao mesmo que o DDD , mas não ha influencia. São duas coisas usando o mesmo conceito. O ponto é que este novo padrão precisa ser alimenta com informações do dominio ( do dominio de dados, ok, mas do dominio , non the less)
Para isso as Annotações cairam que nem uma luva embora o XML seja suportado. Porquê ? Primeiro porque as anotações são intrusivas e deixam o mesmo rastro que o CMP e o EJB <=2 , só que de um jeito menos melequento. Segundo porque o hibernate funcionava com XML nas suas versões pre-JPA.

O DomainStore é um DataMapper com conhecimento do dominio. Quão poderoso é isso ? muito poderoso.
Lá no fundinho das 7 camadas de tinta existe o DAO, mas ele é totalmente irrelevante no dia a dia ( a menos que queira implementar JPA para XML… ).

Ok, depois disto acho que é claro que o DAO foi substituido pelo DomainStore. Um padrão mais moderno , mais robusto e mais versátil. O DAO como o conheciamos não é mais relevante. O seu unico uso hoje em dia é para comunicar com sistemas legados.

Então, para aplicações novas Java 6 e tudo isso não se usa DAO, se usa DomainStore, no corpo do Hibernate ou do JPA. Este é o padrão real para as aplicações modernas.

“Ah! mas eu preciso ter uma camada de indireção para poder substituir o Hibernate/JPA por outra coisa” … boa sorte. Primeiro que essa camada de indireção é complexa p’ra caramba de construir e segundo é inútil se usar hibernate/JPA. Porque o domainStore precisa de info do dominio via anotações ou xml ele sempre estará atrelado do dominio. É muito simples. Pegue um sistema que tenha com hibernate e tente colocar uma camada entre ele e o resto. Depois disso tire o hibernate e coloque outra coisa. Consegue ? … e aqueles anotações lá nas entidades ? como substitui isso ? …

Concluindo: não ha necessidade de uma camada de indireção porque nunca você fará uso dela.

Na analise do Fowler para o Repository que tem o papel de concentrar a montagem das queries isso ainda continua. O DomainStore ainda precisa ser “queriable”. Exactamente ai que o Hibernate bate o JPA e por isso que o JPA 2 vem com a Criteria API. Porque precisa colocar a criteria API ? Porque ela é forma natural de pesquisar um DomainStore. Assim o Repositorio que hoje monta SQL ou Hibernate Criteria, amanha montará JPA 2 Criteria. Acima do repositório nada mudou. Dai a sua função de isolador.

No antigamente vc usava JDBC , depois veio o DAO e subimos um nivel de abstração. Hoje tem um DomainStore dois niveis acima. Faz algum sentido colocar o hibernate dentro de um DAO novamente ? é como colocar um pneu de formula 1 dentro de um pedaço de madeira e acoplar numa carroça. Isso não é forma de fazer sistemas modernos.

Não é o Fowler nem minguem que inviabiliza o uso do DAO nas aplicações modernas, especialmente as que usam Hibernate ou JPA (qq outro DomainStore de mercado). É a própria evolução da tecnologia e dos conceitos teóricos por detrás dela. É a própria historia que coloca o DAO na geladeira.

Foi o padrão DomainStore que matou o DAO , não eu, nem o Fowler, nem ninguém.

R

Aí vai depender que Domain Store você se refere.
Se for ao definido no Core JEe Patterns, não ele só não matou como ainda o utiliza/sugere a utilização do DAO como StoreManager.
http://www.corej2eepatterns.com/Patterns2ndEd/DomainStore.htm

S

Aí vai depender que Domain Store você se refere.
Se for ao definido no Core JEe Patterns, não ele só não matou como ainda o utiliza/sugere a utilização do DAO como StoreManager.
http://www.corej2eepatterns.com/Patterns2ndEd/DomainStore.htm

Para bom entendedor meia palavra basta.
Acho que é claro que quando eu disse “matou” me referi ao uso pelo programador. Além de que eu falei várias vezes que o DAO continua sendo usado dentro do DomainStore.

O ponto é que o programador não precisa implementar ou invocar o DAO nunca mais.

R

Antes este fosse o único problema.

Quando desenvolvemos com frameworks como JPA ou Hibernate nós mudamos nossa maneira de trabalhar com os objetos de negócios. Passamos a considerar relacionamentos LAZY e/ou EAGER, contexto de persistência, dirty checking e features interessantes do framework ORM. Isso claro, se estivermos interessados em aproveitar o que o ORM pode nos oferecer de melhor. Além de que algumas features acabam refletindo noutras camadas. como a preocupação quanto a lazy-loading ou não.

Por isso simplesmente substituir a implementação do DAO não funciona como gostaríamos quando trabalhamos com algum ORM decente.

E

Olá tudo bem, pesquisei durante uma semana e nada, podem me ajudar ?

tenho o seguinte DAO

public class PRT_PROCESSOS_DAO extends GenericDAO {

    public PRT_PROCESSOS getProcesso(Integer codEmp, Integer ano, Integer codProc) {
        return (PRT_PROCESSOS) getPurePojo("from PRT_PROCESSOS where processoPK.PRC_CODEMP = ?"
                + " and processoPK.PRC_CODANO = ? and processoPK.PRC_COD = ?", codEmp, ano, codProc);
    }

irei mudar esse select para pegar todos os processos de uma determinada empresa no qual informo via cnpj na tela. Me ajudem a pegar o resultado desse sql que vou fazer e colocar em uma lista para depois eu pegar a lista e por num tabela. Já li sobre listas coleções etc, preciso de um exemplo, nao quero nada pronto só um exemplo pra me basear.

Obrigado

Criado 15 de junho de 2009
Ultima resposta 4 de dez. de 2012
Respostas 46
Participantes 11