DAO com IoC

33 respostas
saoj

Estou fazendo um DAO, e quero que ele não fique acoplado a nada além de uma Connection do banco de dados ou a uma Session do Hibernate.

Não quero que ele seja acoplado a um HibernateUtil por exemplo, que pega a session do ThreadLocal.

Quem injetaria a session no DAO ???

O container ???

Um filtro ???

A action ???

Se for o container, acho que dá para fazer a mesma coisa mais simplesmente com um filtro né ? Mas aí o filtro teria que acessar diretamente o DAO que está na action.

A última opção é o filtro passar para a action que vai passar para os seus DAOs. Mas não sei se isso é legal…

33 Respostas

louds

Com IoC a regra é: quem cria configura.

Salvo os casos onde vc faz attachments de objetos externos ao container.

saoj

Então pode ser as actions que estão criando os DAOs ???

Ou devo criar os DAOs em factories, e elas que se encarregarão de configurar isso ???

É boa prática passar a session para a action (via filtro) e essa por sua vez injeta no model ???

Thiago_Senna

Se eu naum usar IoC eu acharia que usar factories seria a idéia mais atraente.

Mauricio_Linhares

Opa Sérgio,

Ó, pressupondo que o DAO vai ser um Singleton (normalmente ele não tem estado), encontramos um problema, porque a sessão do Hibernate ou uma conexão JDBC não são singletons e eu preciso sempre pegar um “novo” objeto desses a cada chamada de método dentro do DAO (mesmo que não seja exatamente um novo).

No Spring, agente pode usar “injeção de método”, onde sempre que eu chamar um “getConnection()” ele vai injetar a dependência Connection que esse método precisa, mas eu ainda tenho que chamar o “get”, do mesmo jeito que eu faria com a HibernateUtil. Não ganhei nada fazendo isso.

Já se o DAO não for um singleton, ele pode ter a conexão ou a sessão dentro dele mesmo, mas quando iniciar ela? Quando terminar? Quem é que vai chamar os métodos de iniciar e terminar? O meu código? Ops, feio, imagina chamar um método “connect()” e depois “commit()” num DAO? Matou a abstração toda dele.

Porque você não quer que uma hipotética implementação do DAO usando Hibernate não use uma HibernateUtil da vida?

saoj

Não quero acoplar o DAO a nada… Nem ao HibernateUtil… Quero que ele seja totalmente independente…

O que eu gostaria de fazer, e nao sei se é boa prática é:

DAO tem setConnection ou setSession do Hibernate.

Filtro Abre uma session ou connection e passa para a action

Action injeta Connection ou session do Hibernate via os setters acima.

Daos são executados, assim como o MODEL

Action retorna

Filtros fecham sessão, retornam connection, etc e tal.

Isso na prática me parece perfeito !!! E na teoria, faz sentido?

Thiago_Senna

humm… o que acham??

public ClienteDAO {
  
  private PersistenceManager pm;

  public void setPersistenceManager(PersistenceManager pm) {
      this.pm = pm;
  }

  public void save(Cliente cliente) {
     pm.save(cliente);
  }
}
Mauricio_Linhares

Eu já diria o contrário, na teoria parece perfeito, na prática é que a coisa vai ser complicada. Onde vai ficar as ThreadLocal que vão guadar a sessão e a transação do Hibenate? No DAO? Não dá, pode haver mais do que um DAO trabalhando dentro de uma mesma requisição.

Se a ThreadLocal ficar dentro do filtro, ainda pode ser que funcione, mas os DAOs obrigatoriamente deixam de ser singletons e vão ter que guardar a sessão e a transação em algum lugar, e se houverem vários DAOs na mesma requisição, todos vão ter que ser injetados.

Na boa, use Spring, isso já tá pronto :mrgreen:

Mauricio_Linhares
Thiago Senna:
humm.. o que acham??
public ClienteDAO {
  
  private PersistenceManager pm;

  public void setPersistenceManager(PersistenceManager pm) {
      this.pm = pm;
  }

  public void save(Cliente cliente) {
     pm.save(cliente);
  }
}

Quem vai "settar" o PersistenceManager?

Como é que ele recebe as conexões?

O DAO é Singleton?

Onde tudo se "fecha"?

Thiago_Senna

Poderia ser via IoC! Eu prefiro esta abordagem.

Particularmente gosto muito da idéia que você tem defendido e n posts e tópicos até aqui. Adoro o ThreadLocal.

Ou seja, a classe que implementa o PersistenceManager irá buscar a session ou a conexão em um HibernateUtil, ou ConexãoUtil que me traz a session ou connection da thread corrente!

Não!

por exemplo…

pm.dispose()

Eu colocaria o comando pm.dispose() no filtro, da mesma que forma que vc sugeriu em n tópicos esta semana :smiley:

Está legal fazer assim o gerenciamento da persistência? Tem alguma crítica ou possível melhoria?

Abraços!
Thiago Senna

Thiago_Senna

nada melhor do que um exemplo concreto, naum é?

public class HibernatePersistenceManager implements PersistenceManager {
	
	Log log = LogFactory.getLog(HibernatePersistenceManager.class);
	
	public void save(Object obj) {
		try {
			Session session = HibernateUtil.getSession();
			session.saveOrUpdate(obj);
		} catch (HibernateException e) {
			log.error(e.getMessage(), e);
			throw new PersistenceException(e.getMessage(), e);
		}
	}

Um exemplo de caso de teste usando o HibernatePersistenceManager

public void testeCreateAndLoadUserStorie() throws HibernateException {
		
		PersistenceManager pm = new HibernatePersistenceManager();	
		
		UserStorie us = new UserStorie("Sample user storie");
		us.setContent("content of sample user storie");
		us.setEstimatePoints(2.5f);		
			
		pm.save(us);
		
		HibernateUtil.getSession().flush();
		
		Long usId = new Long(us.getIdent());
		us = null;
		
		us = (UserStorie) pm.load(usId);
		
		assertNotNull(us);
		assertEquals(us.getTitle(), "Sample user storie");
		assertEquals(us.getContent(), "content of sample user storie");
		assertTrue(us.getEstimatePoints() == 2.5);
		
		HibernateUtil.getSession().flush();
		HibernateUtil.closeSession();
	}

Como é apenas o teste, eu inseria as dependências no próprio teste mesmo...

Mas no código de produção seria IoC..

Abraços!
Thiago

louds

Mauricio, um DAO não precisa ser um singleton, de fato, não existe motivo para ele ser.

Me responde uma pergunta, se existirem mais de uma instância da mesma classe de DAO, podemos ter problemas? Se a resposta for diferente de um categórico SIM, já tá decidido que não deve ser um Singleton.

DAOs simples costumam não ter estado, mas não é incomum usar eles para abstrair esquemas toscos e legados.

Tive um caso que dependendo de algumas variaveis de ambiente, o usuário logado as tabelas e o mecanismo de busca mudavam. Ou seja, o DAO não era 100% stateless por que essas informações vinham no construtor dele.

saoj

Começo a ver que foi criado uma tempestade num copo d’água devido ao código abaixo:

Input input = new InputMap();
  input.setValue("username", "sergio");
 
  input.setValue("session", hibernate session aqui);
  
  Context session = new ContextMap();
  session.setValue("user", new User("sergio")); // contexto de sessão
  
  Context application = new ContextMap();
  application.setValue("algumacoisa", "hello"); // contexto da aplicacao
  
  Locale loc = new Locale("pt", "BR"); // toda action possui o seu locale...
  
  Action action = new HelloWorldAction(); // note que aqui podemos usar a interface
 
  prepareAction(action, input, session, application, loc);
 
  assertEquals(Action.SUCCESS, action.execute());

Nele eu estou passando uma session do hibernate para dentro de uma action, e isso foi motivo de muita discussão.

Estou pensando no seguinte cenário:

:arrow: Sistema em produção:

  • Filtro pega uma sessão da maneira que for (HibernateUtil, ThreadLocal, etc.) e colocar no INPUT da action ou injeta num setter da action. Se fosse Model-Driven injetaria diretamente no model (via getModel() da action)

  • Action de posso da sessão, injeta no model ou DAO. A action não faz uso da session, apenas a repassa (injeta) para o model ou DAO.

  • Action executa.

  • Filtro fecha a sessão.

OBS: Quem cuida da transação é o MODEL ou DAO. Isso está fora da discussão aqui.

:arrow: Ambiente de Testes

  • Não há filtros.

[color=red]- Não precisa injetar a session, pois o DAO ou o MODEL vão estar MOCKEADOS e não vão acessar o banco e dados.[/color] Pode meter um null na session e passar para a action.

  • Executa-se a action

  • Testa-se o resultado da action.

Ficou um pouco esquisito o item em vermelho, mas como o objeto vai estar mockeado pode até fazer sentido.

A pergunta meio que persiste. Se não for a action que vai injetar as coisas no MODEL / DAO que ela contem, quem fará isso.

Ao vamos acoplar o HibernateUtil dentro do DAO e dane-se… (naoooooo… agora o chato sou eu! :lol: )

Thiago, a sua solução é bem legal. Só que acho que vc está acoplando o PersistenceManager a sua action. Vai ter que levar ele com vc para onde vc for.

A minha situação seria melhor descrita assim:

Tenho um model que precisa de uma session para fazer consultas loucas no banco-de-dados. Não tem DAO aqui. O meu model pode usar o HibernateUtil e ficar pra sempre dependente dele, ou pode receber via injection a session e ficar totalmente desacoplado. Se fosse a segunda opção, quem passaria a session pra ele? Pode ser o filtro ?

Thiago_Senna

Depende de como vou usar o PersistenceManager.

Meu action só conhecerá o PersistenceManager se eu usá-lo na minha action, mas naum é isso que eu pretendo fazer. Quem conhece meu persistence manager são meus daos, ou melhor ainda, meu modelo… já que o PersistenceManager é uma interface.

Assim, independe se o meu modelo teráum método save ou o dao!

Na prática, eu iria utilizar chamadas ao persistence manager apenas para inciar transação e finalizar tranzações nos filtros! Portanto, minha action nem iria saber que o PM existe… na verdade, não deve!

Thiago_Senna

humm…

repare que para criar um Mock para meu PersistenceManager é brincadeira de criança!

public void testSaveUserStorie() {

UserStorie userStorie = null;
Mock mockPM = null;

userStorie = new UserStorie("Saving a user storie");
userStorie.setContent("XPTurbine must save user stories.");
userStorie.setEstimatePoints(2.5f);
		
mockPM = new Mock(PersistenceManager.class);	
mockPM.expectVoid("save", userStorie);
		
ReleaseManager manager = new ReleaseManager();
manager.setPersistenceManager((PersistenceManager)mockPM.proxy());
		
manager.saveUserStorie(userStorie);
mockPM.verify();
	}

Lembra quando conversamos sobre o método verify?
Aqui neste exemplo mostra o quanto ele ´útil.
Como meu objetivo é testar o método saveUserStorie(userStorie) da classe ReleaseManager, entaum o que me importa é que quando eu chama o saveUserStorie, ele acesse entaum o método save de persistenceManager, o qual sua implementação no caso do teste é o Mock.

Isso se dará de forma muito parecida quando eu estiver trabalhando com uma Action.

Ao testar uma action, eu não me importaria, e não seria trabalhoso criar um mock simples como este. Mas se no meu teste além de criar este mock eu também tiver que criar outros 2 Mocks, será no mínimo cansativo! :wink:

Abraços!
Thiago Senna

saoj

Desculpa !!! Falei errado! Acopla no Model o persistence manager.

De novo, não sei se isso é ruim. Talvez acoplar persistenceManager ou HibernateUtil no seu model seja a melhor coisa a fazer. E IoC que se dane…

Thiago_Senna

saoj:
“Thiago”:

Portanto, minha action nem iria saber que o PM existe… na verdade, não deve!

Desculpa !!! Falei errado! Acopla no Model o persistence manager.

De novo, não sei se isso é ruim. Talvez acoplar persistenceManager ou HibernateUtil no seu model seja a melhor coisa a fazer. E IoC que se dane…

Sim… eu acoplo o persistence manager ao meu modelo. Mas ainda assim tem como eu fugir deste acoplamentozinho usando um componente para estar realizando a persistência.

Mas o que é legal é que meu PersistenceManager é uma interface, e ai, basta eu criar suas devidas implemetações… HibernatePersistenceManager, MySQLPersistenceManager, XMLPersistenceManager e blá blá blá!

Agora com o persistence manager, o hiberte util estaria sendo usado pelo persistence manager. Assim ninguém mais saberia que ele existe!

Thiago

louds

Um erro que todo mundo comete quando usa o padrão DAO é tentar colocar o gerenciamento da transação nos DAOs. Simplesmente não fica natural, fica bem melhor em outro layer da aplicação.

Mauricio_Linhares

Por isso que eu gosto dos DAOs como singletons, não tem perigo do cara querer fazer isso :mrgreen:

Lá no seu problema realmente tinha que ser não-singleton, mas eu acho que isso é mais exceção do que regra :smiley:

pcalcado

Eu posso estar meio bebeado aidna, mas não entendi a temática principal: Sérgio, você quer desacoplar um dao da persistência? Se a resposta for sim, acho que você rpecisa de algo mais alto-nível que um DAO, ou simplesmente uma interface ClienteDao que tenha os métodos e seja implementada para Hibernate do modo X, para JDBC do modo Y…

O louds está completamente certo em afirmar que não existe necessidade para um DAO ser singleton. Fazer isso cheira variável global.

Não se deve confundir singleton (X instâncias e ponto final) e Value Object (não o da Sun, o do Domain-Driven). No caso não sei se vale chamar de value object, mas são simplesmente objetos sem identidade. Se houverem mil isntâncias disponíveis, tanto faz qual eu use para realizar a operação, ou se crie um novo, se cada vez utilize um diferente…esses objetos não têm identidade.

Mauricio_Linhares

Realmente, Value Object fica mais bem explicado.

Mas eu ainda acho que DAOs com estado são um bixo perigoso, o que vai ter de gente querendo meter controle de transações nos DAOs não vai caber no gibi :mrgreen:

saoj

Acho que eu pensei errado.

Imagina um MODEL, não um DAO, que precisa de uma session do Hibernate.

Vc pode meter um HibernateUtil lá dentro ou pode injetar a session via setter.

O que é mais recomendável ???

Eu prefiro injetar a session, pois eu posso controlar a configuração do Hibernate junto com o restante da configuração da minha aplicação, e não numa classe estática.

Injetando eu tb posso fazer isso num filtro, o que me garante o fechamento automático, etc.

Aí o HibernateFilter náo ficaria todo sem sentido e poderia ser usado pela action para injetar a session no model.

O que faz sentido de tudo isso ?

Rubem_Azenha

lendo a definição de DAO, pensei nesta DAOFactory:

public class ClientDAOFactory{

private Map daos;

private static ClientDAOFactory instance;

privete ClientDAO createDAO(String daoName){

Class daoClass = daos.get(daoName);

return daoClass.newInstance();
}

private ClienteDAOFactory(){

daos = new HashMap();
daos.put("OracleDAO",OracleDAO.class);
daos.put("HiberanteDAO",HiberanteDAO.class);
//...
}

public ClientDAOFactory getInstance(){

if (instance == null)
   instance = new ClientDAOFactory();
return instance;
}


}



}
Mauricio_Linhares

saoj:
Acho que eu pensei errado.

Imagina um MODEL, não um DAO, que precisa de uma session do Hibernate.

Vc pode meter um HibernateUtil lá dentro ou pode injetar a session via setter.

O que é mais recomendável ???

Eu prefiro injetar a session, pois eu posso controlar a configuração do Hibernate junto com o restante da configuração da minha aplicação, e não numa classe estática.

Injetando eu tb posso fazer isso num filtro, o que me garante o fechamento automático, etc.

Aí o HibernateFilter náo ficaria todo sem sentido e poderia ser usado pela action para injetar a session no model.

O que faz sentido de tudo isso ?

O que é um model?

O que é que você quer abstrair? O Hibernate ou o acesso a um mecanismo de persistência?

Mauricio_Linhares
microfilo:
lendo a definição de DAO, pensei nesta DAOFactory:
public class ClientDAOFactory{

private Map daos;

private static ClientDAOFactory instance;

privete ClientDAO createDAO(String daoName){

Class daoClass = daos.get(daoName);

return daoClass.newInstance();
}

private ClienteDAOFactory(){

daos = new HashMap();
daos.put("OracleDAO",OracleDAO.class);
daos.put("HiberanteDAO",HiberanteDAO.class);
//...
}

public ClientDAOFactory getInstance(){

if (instance == null)
   instance = new ClientDAOFactory();
return instance;
}


}



}

Prefiro mil vezes IoC a usar factories.

Guilherme_Silveira

da uma olhada no ioc do projeto do guj no java.net. tem o que voce ta querendo.

Guilherme_Silveira

mas no caso as duas opcoes sao hibernate e prevayler. nao sei como esta o codigo do prevayler mas da para se basear no factory de la para abstrair mais

Thiago_Senna
microfilo:
lendo a definição de DAO, pensei nesta DAOFactory:
public class ClientDAOFactory{

private Map daos;

private static ClientDAOFactory instance;

privete ClientDAO createDAO(String daoName){

Class daoClass = daos.get(daoName);

return daoClass.newInstance();
}

private ClienteDAOFactory(){

daos = new HashMap();
daos.put("OracleDAO",OracleDAO.class);
daos.put("HiberanteDAO",HiberanteDAO.class);
//...
}

public ClientDAOFactory getInstance(){

if (instance == null)
   instance = new ClientDAOFactory();
return instance;
}

}

}

Só uma sugestão de melhoria!

Não acho muito legala idéia de você criar um OracleDAO, HibernateDAO, MySQLDAO.

Essa sua fábrica deveria saber criar um ClienteDAO, AlunoDAO, EmpresaDAO e ai por diante. Em um arquivo de properties, tu diz qual é a implementação do DAO que tu prefere, por exemplo

FrabricaDeDAO.properties
implementacao_do_DAO = MySQL
#implementacao_do_DAO = Oracle
#implementacao_do_DAO = Hibernate

Como a linha do MySQL está descomentada, isso quer dizer que sua fábrica quando criar um ClienteDAO, ele irá instanciar um ClienteDAO para acessar um banco de dados MySQL.

Para que vocÊ mude a implementação da sua aplicação para Hibernate por exemplo, basta descomentar a linha do Hibernate e comentar a do mysql. Pronto, agora sua aplicação suporta Oracle.

Normalmente o pessoal implementa usando a AbstractFactory e FactoryMethods! Vale a pena conferir! :wink:

Thiago Senna

saoj

Maurício Linhares:

O que é um model?

O que é que você quer abstrair? O Hibernate ou o acesso a um mecanismo de persistência?

Model = Modelo de negócios

Pense que tenho um modelo que precisa fazer mil consultas no banco de dados e pra isso vou usar a session do hibernate.

Quero desacoplar essa session dele, isto é, não quero que ele use HibernateUtil. Quero injetar essa session nele quando for executar esse modelo.

E a pergunta é. Posso (e devo) fazer isso via action, isto é, action injeta no model ???

Não achei. Qual URL ?

Mauricio_Linhares

saoj:
Maurício Linhares:

O que é um model?

O que é que você quer abstrair? O Hibernate ou o acesso a um mecanismo de persistência?

Model = Modelo de negócios

Pense que tenho um modelo que precisa fazer mil consultas no banco de dados e pra isso vou usar a session do hibernate.

E a pergunta é. Posso (e devo) fazer isso via action, isto é, action injeta no model ???

Um objeto do modelo precisa fazer mil consultas no banco? Acho que tem alguma coisa vazando por aqui. Quem tem que fazer consultas no banco é o Dao ou seja lá o que for a camada de persistência, não o modelo.

O action pode injetar a sessão no Dao, não tem problema, mas se houverem vários Daos ele vai ter que injetar em todos os vários.

saoj

Vão haver casos de consultas complexas e outras coisas a mais que usar DAO não será a opção !!! Aí vc pode sim criar um modelo que vai no banco pegar o que ele precisa, usa JDBC ou HSQL, para calcular o que vc precisa.

Mauricio_Linhares

saoj:
Maurício Linhares:

Um objeto do modelo precisa fazer mil consultas no banco? Acho que tem alguma coisa vazando por aqui. Quem tem que fazer consultas no banco é o Dao ou seja lá o que for a camada de persistência, não o modelo.

Vão haver casos de consultas complexas e outras coisas a mais que usar DAO não será a opção !!! Aí vc pode sim criar um modelo que vai no banco pegar o que ele precisa, usa JDBC ou HSQL, para calcular o que vc precisa.

Como por exemplo :?:

Guilherme_Silveira

guj.dev.java.net? algo do genero…

noelrocha

Maurício Linhares:
Opa Sérgio,
Quem é que vai chamar os métodos de iniciar e terminar? O meu código? Ops, feio, imagina chamar um método “connect()” e depois “commit()” num DAO? Matou a abstração toda dele.

E quem é que vai abir a transação e dar um commit ou rollback e fechar a sessão?

Criado 17 de julho de 2005
Ultima resposta 17 de jul. de 2005
Respostas 33
Participantes 8