DAO com IoC

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…

Com IoC a regra é: quem cria configura.

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

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

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

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?

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?

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);
  }
}

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:

[quote=Thiago Senna]humm… o que acham??

[code]
public ClienteDAO {

private PersistenceManager pm;

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

public void save(Cliente cliente) {
pm.save(cliente);
}
}
[/code][/quote]

Quem vai “settar” o PersistenceManager?

Como é que ele recebe as conexões?

O DAO é Singleton?

Onde tudo se “fecha”?

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

nada melhor do que um exemplo concreto, naum é?

[code]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);
	}
}

[/code]

Um exemplo de caso de teste usando o HibernatePersistenceManager

[code]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();
}[/code]

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

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.

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 ?

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!

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

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…

[quote=saoj][quote=“Thiago”]
Portanto, minha action nem iria saber que o PM existe… na verdade, não deve!
[/quote]

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…[/quote]

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

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.

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:

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.