Hibernate, Sessões e transações

Olá galera,

estava fazendo um DAO genérico pra uma app desktop em java e comecei a pensar numas coisas curiosas. Com certeza a turma do GUJ deve ter bons conceitos disso. Tipo, num DAO genérico de uma app desktop sem Spring pra fazer Injeção de dependência, mas com Hibernate 3, eu preciso ficar toda hora: abre a sessão, abre a transação, realiza a operação(insert, delete, merge etc.), dá um flush na sessão, commit na transação e fecha a sessão. Aí surgem as idéias e as dúvidas:

1 - É terrível ficar repetindo código que não é de responsabilidade do DAO. Tipo getSession.flush() e tx.commit() e tal… Gerenciar transação não é papel do DAO. Td bem, com Spring é uma blz! Ele resolve isso fácil pra mim! Mas e sem spring? E outra, da forma como tá eu sempre terei uma transação por sessão. Não é muito flexível isso. Existe uma série de problemas em fazer isso. Oq vcs acham/costumam fazer?

2 - Tendo em vista a situação acima, decidi testar a mesma sessão pra cada DAO instanciado. Ou seja, eu nunca dou close() na sessão. Por outro lado, sempre dou clear() ao fim de cada método do DAO. Já nos métodos insert, delete e merge eu abro uma transação antes e fecho no mesmo método que a sessão é limpa. Daí o código não fica tão repetitivo (mas ainda fica um pouco) e eu posso usar n transações pra 1 sessão. Criei uma classe TransactionManager pra tratar isso fora do DAO. Essa estratégia de aproveitar a mesma sessão, é boa ou é sujeira?

3 - Pra resolver esse problema de forma simples eu só consigo pensar em AOP. Executar sempre antes dos métodos do DAO os métodos que iniciam a transação/sessão e após tudo isso, fechar. Alguém usa isso aqui? É bom?

É isso, quem puder esclarecer, fico grato.

[]s.

Fala Tiago,

Quanto a sua pergunta número 2, eu já usei esse método: de sempre usar a mesma sessão, apenas dando clear, e comigo não funcionou. Vira-e-mexe, o Hibernate acusa que há objetos em duplicidade no “cache”. Depois descobri que, se você desativar o cache de segundo nível, o problema não mais acontece. Só que por via das dúvidas, resolvi seguir o jeito tradicional: abre sessão, abre transação, flush, commit, clear e close. Note que antes do close, ainda dou um clear, só pra garantir.

Na sua pergunta 3, não sei se entendi, mas eu estou desenvolvendo um sistema usando Struts 2 e Hibernate, e na minha classe DAO (que é genérica), eu tenho apenas os métodos de criar, atualizar, deletar, obter e listar. Eu faço mais ou menos assim: no construtor da minha classe DAO genérica, passo um objeto de sessão já criado, depois disso, chamo o método da minha classe DAO, e em seguida, flush, commit, clear e close. Fica mais ou menos assim:

		Session sessaoDao = new HibernateUtil().getSession();
		Transaction transacao = sessaoDao.beginTransaction();
		transacao.begin();
		try
		{
			Dao dao = obterDao(sessaoDao);
			dao.criar(objetoModelo); // Esse objetoModelo, vem da minha sessão do browser.
			sessaoDao.flush();
			transacao.commit();
		} 
		catch (Exception e)
		{

			transacao.rollback();
			e.printStackTrace();
		}
		finally
		{
			sessaoDao.clear();
			sessaoDao.close();
		}

E pelo menos para fazer as telas de CRUD do meu sistema, nem preciso repetir mais, pois esse código está todo numa classe action mãe. Só chamo os métodos actions e passo a classe que desejo persistir. O resto, o Hibernate com o Struts 2 se viram! rsrsrsrs!

Agora, que ficar repetindo código é escroto, isso é. Mas acho que há jeitos de se contornar. Eu por exemplo, como disse acima, coloquei numa classe mãe. Claro, em algumas partes do projeto, principalmente nas transações, não teve jeito: tive que lançar mão do código acima, e fazendo até algumas modificações, mas de resto, achei bem satisfatório!

Valeu, espero ter ajudado!

Vlw por responder Victor!

Então, eu não tinha pensado na situação encontrada por vc (objeto já existente no cache)! Eu estava com o second level cache desabilitado, por isso nunca iria encontrar esse conflito. Mas ontem eu configurei o second level cache e provavelmente ia mesmo dar esse erro. TÔ fechando minhas sessões agora. Abro uma sessão por método de serviço e a fecho em seguida. =]

Quanto a repetir código, é terrível. Eu tentei essa abordagem q vc citou (passar um Session já criado pra instanciar o DAO), mas ainda não achei suficiente. Como eu tinha mencionado AOP antes, consegui encontrar um artigo mt bom aqui mesmo no GUJ. Ele fala do Method Interceptor pra interceptar métodos transacionais no Hibernate. No caso, eu tive q anotar os métodos transacionais do meu controlador e antes de executar os métodos eu abro uma transação e após o save, delete, merge etc. eu comito ou dou rollback (e fecho a sessão agora =D). É basicamente o q o Spring faz pra gerenciar transações e acho mt bom. Mas, se eu tivesse num projeto Struts/Hibernate como o seu, eu integraria o Spring (o meu é desktop). Enfim, obrigado pela resposta e esclarecimento!

[]s.

Tiago,

Quando eu precisei usar isso eu tinha uma classe DAO genérica com os métodos save, update, find, load e delete (que delegavam operações para a session do Hibernate). Daí eu criava os DAOs específicos herdando desta classe.

Para o gerenciamento de transações eu usei a API CGLIB. Ela permite interceptar métodos. Inclusive escrevi um artigo sobre isso a algum tempo atrás. Se você quiser acessar, aqui está: http://tosin.com.br/blog/cglib.pdf

Outra coisa que eu usei foi uma variável ThreadLocal para gerenciar a session do Hibernate. Usando isso e o CGLIB você consegue criar uma solução onde a session do Hibernate sempre é fechada corretamente e não existe o perigo de a mesma thread abrir mais de uma session. Comentei também sobre isso num post que eu fiz há um tempo atrás (era pra JPA mas a ideia é a mesma): http://tosin.com.br/blog/2009/08/gerenciando-os-entity-managers-da-jpa.html

Eu também uso uma transação por session. Foi o modelo que apresentou melhores resultados.

Abraço

Então, foi exatamente isso q fiz. Method Interceptor com CGLib e um DAO genérico do qual os outros DAOs herdam. Esse artigo q eu te falei q li no GUJ é muito parecido com o seu citado acima. No entanto, pra esclarecer alguns detalhes, já baixei esse pdf pra ler. Ele explica alguns detalhes q eu considero importante. Muito obrigado!
A única coisa q eu não gostei é que não é possível com a cglib vc conseguir criar objetos com construtor private, um singleton por exemplo. Aí tive de “empurrar” um construtor protected na classe gerada. O efeito é o mesmo na prática, mas conceitualmente… sabe alguma outra forma de contornar isso?

Tb estou usando uma transação por session. Acontece q antes de refinar meu sistema, eu utilizava uma operação(save, merge, delete etc.) por transação. Agora uso uma transação por session mas com várias operações dentro de uma mesma transação (me pareceu bem óbvio o motivo disto).

[]s.

Vc fala como se o spring não podesse ser usado em desktop… ele pode.
Use o spring e seja feliz.

Sérgio, a única coisa nesse projeto que eu vou precisar de Spring é pra gerenciar transações. Pesando bem as coisas, acho q não compensa. Mas de qq forma, vc tem algum material pra usar Spring pra desktop?

[]s

[quote=Tiago Farias]Sérgio, a única coisa nesse projeto que eu vou precisar de Spring é pra gerenciar transações. Pesando bem as coisas, acho q não compensa. Mas de qq forma, vc tem algum material pra usar Spring pra desktop?
[/quote]

Vc que sabe o aproveitamento que faz do spring. Eu acho que usar o Injetor de Dependencia é um despredicio.
O uso do spring em desktop é igual ao em web. Vc cria um bean context no inico (no main) , carrega-o, e depois usa.

[quote=Tiago Farias]Sérgio, a única coisa nesse projeto que eu vou precisar de Spring é pra gerenciar transações. Pesando bem as coisas, acho q não compensa. Mas de qq forma, vc tem algum material pra usar Spring pra desktop?

[]s[/quote]

Não compensa pq? Se vai tornar as coisas mais simples? Leia mais sobre o Spring e você vais ver que ele não é coisa complicada não. Controlar transação na mão que é complicado e arriscado.

http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html

Tiago, Spring não muda em praticamente nada referente a web e desktop
A unica diferença eh na hora de carregar o Application Context

Na web vc configura no web.xml
Em desktop vc cria uma classe para carregar

Segue um exemplo que utilizo em um projeto desktop

[code]import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
*

  • @author Thiago
    */
    public class SpringUtil
    {
    private static ApplicationContext ac = null;
    private static final Logger log = Logger.getLogger( SpringUtil.class );

    public static ApplicationContext getApplicationContext()
    {
    if ( ac == null )
    {
    log.info( “Carregando Framework Spring” );
    try
    {
    List configs = new ArrayList();
    configs.add( “conf/application-context-business.xml” );
    configs.add( “conf/application-context-dao.xml” );

             String[] array = new String[configs.size()];
    
             configs.toArray( array );
    
             ac = new ClassPathXmlApplicationContext( array );
             log.info( "Spring carregado com sucesso" );
         }
         catch ( Exception e )
         {
             e.printStackTrace();
         }
     }
    
     return ac;
    

    }
    }[/code]

No meu projeto desktop utilizo annotations para mapear os business ou qualquer outro tipo de objeto
e para acessar os beans simplesmente coloque ou @Resource quando a classe também for um componente do Spring
Ou SpringUtil.getApplicationContext.getBean(“nomeDoBeanMapeado”) fazendo o cast para o objeto desejado.

Opa! Gostei do exemplo! Vlw :wink:

Então Alexandro, eu trabalho com Spring em projetos web. Eu sei como funciona. Sei q é simples de configurar. No meu caso, eu precisaria configurar o Spring só pra usar o suporte as transações. É meio q desperdício… não gostei da idéia. Mas enfim, uso Spring sempre q posso.
Ainda mais, Method Interceptor é muito fácil e não é arriscado. Além do q, não é preciso configurar nada. No final das contas, eu não controlo as transações na mão. Fica igual a usar aspectos. Um método q roda antes de um método transacional iniciar e logo depois (pra fechar a transação). 2 classes pequenas pra fazer isso. E sem perder simplicidade.
Não acho q é perfeito, afinal usar Spring tem suas vantagens. Se esse projeto fosse expandir, por exemplo. Mas, me apegando ao princípio da simplicidade, a probabilidade desse projeto expandir é pequena, então o Method Interceptor vai servir.

Agradeço mt as idéias de vcs! =]

Mas para usar o Method Interceptor vc esta usando EJB 3.0, Ok?

Nada de EJB 3. Só CGlib. =]

Alguém postou esse link aqui de como fazer isso.

http://tosin.com.br/blog/cglib.pdf

[quote=Tiago Farias]Nada de EJB 3. Só CGlib. =]

Alguém postou esse link aqui de como fazer isso.

http://tosin.com.br/blog/cglib.pdf
[/quote]

E isso é mais simples que configurar o Spring? Pode até ser, mas o Spring esta ai pronto, testado e muito bem documentado…

Mas blz.