Open Session View – Hibernate Solução

solutionproblem

olá Pessoal,

No post de hoje vou falar de um problema que todos que estão trabalhando com Hibernate não está livre de se deparar com ele. É o velho LazyInitializationException que acontece e deixa o pobre do desenvolvedor stressado.

Neste post pretendo ser menos teórico e mais prático, pois tem muita coisa na net explicando o porque temos o LazyInitializationException, porém poucos explicaram de forma prática a solução. O mais próximo foi este site. Que se você reparar direito tem um pequeno erro no mapeamento do filtro.

Então aqui vou mostrar como resolver o problema, pois também fui afetado com este problema e levei umas quase 3 horas para resolver. E agora vejo que era algo simples, mas eu precisava entender o por que?! de cada exceção que vinha recebendo. Para este este post usei o post que está no Jboss Community, porém fiz algumas adaptações.

Lets go…

Vou usar a técnica de reutilização da informação então o Paulo da Caelum já fez uma abordagem excelente do porque desse problema , veja.

Desenvolvendo

Se você não quer desenvolver os codes a seguir, então mude o relacionamento para EAGER ao invés de usar o LAZY. Os codes a seguir é útil para quem necessita de ser LAZY o relacionamento.

  1. crie um novo package no seu projeto o meu chamei de : br.com.filtro

O pessoal do Jboss Community não colocou quais classes deveriamos importar, um programador inexperiente pode se atrapalhar e importar classes inapropriadas. Em função disso coloquei abaixo o código completo.

  1. Crie a classe HibernateSessionRequestFilter conforme abaixo: (fique atento ao meu comentário na classe)

    package br.com.filtro;
    
    import java.io.IOException;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.hibernate.SessionFactory;
    import org.hibernate.StaleObjectStateException;
    
    import br.com.dao.DAO;
    
    public class HibernateSessionRequestFilter implements Filter {
    
    	 private static Log log = LogFactory.getLog(HibernateSessionRequestFilter.class);
    
    	    private SessionFactory sf;
    
    	    public void doFilter(ServletRequest request,
    	                         ServletResponse response,
    	                         FilterChain chain)
    	            throws IOException, ServletException {
    
    	        try {
    	          System.out.println("iniciando a transacao com o DB");
    	            sf.getCurrentSession().beginTransaction();
    
    	            // Call the next filter (continue request processing)
    	            chain.doFilter(request, response);
    
    	            // Commit and cleanup
    	            log.debug("Committing the database transaction");
    	          sf.getCurrentSession().getTransaction().commit();
    
    	        } catch (StaleObjectStateException staleEx) {
    	            log.error("This interceptor does not implement optimistic concurrency control!");
    	            log.error("Your application will not work until you add compensation actions!");
    	            // Rollback, close everything, possibly compensate for any permanent changes
    	            // during the conversation, and finally restart business conversation. Maybe
    	            // give the user of the application a chance to merge some of his work with
    	            // fresh data... what you do here depends on your applications design.
    	            throw staleEx;
    	        } catch (Throwable ex) {
    	            // Rollback only
    	            ex.printStackTrace();
    	            try {
    	                if (sf.getCurrentSession().getTransaction().isActive()) {
    	                    log.debug("Trying to rollback database transaction after exception");
    	                    sf.getCurrentSession().getTransaction().rollback();
    	                }
    	            } catch (Throwable rbEx) {
    	                log.error("Could not rollback transaction after exception!", rbEx);
    	            }
    
    	            // Let others handle it... maybe another interceptor for exceptions?
    	            throw new ServletException(ex);
    	        }
    	    }
    
    	    public void init(FilterConfig filterConfig) throws ServletException {
    	        //log.debug("Initializing filter...");
    	       // log.debug("Obtaining SessionFactory from static HibernateUtil singleton");
    	        sf = DAO.getSessionFactory();//vem da minha classe DAO
    	    }
    
    	    public void destroy() {}
    
    }
  2. Agora veja como está meu DAO:

    package br.com.dao;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    
    public class DAO {
    	private static ThreadLocal threadlocal = new ThreadLocal();
    	private static SessionFactory sessionFactory = new Configuration().configure().
    	buildSessionFactory(); 
    
    public DAO() {
    	// TODO Auto-generated constructor stub
    }
    public static  Session getSession(){
    	Session session = (Session) threadlocal.get();
    	if(session == null){
    		session = sessionFactory.openSession();
    		threadlocal.set(session);
    	}
    	return session;
    }
    	public void begin(){
    		getSession().beginTransaction();
    
    	}
    public void commit(){
    	getSession().getTransaction().commit();
    }
    
    public void rollback(){
    	getSession().getTransaction().rollback();
    }
    public void close(){
    	getSession().close();
    }
    public static void shutdown() {
    	// Close caches and connection pools
    	getSessionFactory().close();
    	}
    //passamos ele para o Filter
    public static SessionFactory getSessionFactory() {
    	return sessionFactory;
    }
    public static void setSessionFactory(SessionFactory sessionFactory) {
    	DAO.sessionFactory = sessionFactory;
    }
    
    }

Não precisei alterar nada da configuração padrão do hibernate.

  1. Abra seu arquivo hibernate.cfg.xml e adicione a linha abaixo:

    < property name=”hibernate.current_session_context_class”>thread  < /property>

  2. Salve e execute sua aplicação, veja se os println do Filter serão impressos (claro, os que estão nos catches nem devem aparecer). Em caso positivo está tudo ok.

  3. Agora teste várias vezes sua app e veja se verá o problema com LazyInitializationException. Aqui ele sumiu de vez, graças à deus.

A única coisa que mudei na classe HibernateSessionRequestFilter, foi informar meu SessionFactory e o resto o Filter fez o trabalho dele.

Bom vou ficando por aqui, espero que tenham gostado do post. Não posso deixar de agradecer ao autor Edson Gonçalves que contribuiu bastante para este post, desde a indicação do link, como as explanações pelo mesmo que foi contribuindo  até encontrar a solução, o colega Rafael Viana (GUJ) já tinha passado por um problema parecido e compartilhou sua experiência opinando como poderia ser resolvido.

Fica ai agora a versão em português do problema. Abracos, see you next post.

17 comentários em “Open Session View – Hibernate Solução”

  1. William Morais :
    Opa Camilo, cara, que dica show! me ajudou demais, vlw mesmo!
    só uma observação, depois se puder corrige ai colocando a linha toda que deve ser inserida no arquivo “hibernate.cfg.xml” que é:
    thread
    penei um pouquinho pra descobrir em qual propriedade eu tinha que setar “thread”, mas é só um detalhe pra ajudar os próximos que verem o post!!
    []s
    William Morais

    1. opa! Willian,

      Valeu por avisar, na verdade eu coloquei, porem esses plugins para deixar o code organiado no eclipse é o que mata, ele alterou o conteudo, aff, odeio esses plugins tem hora. Mas, atualizei o post ja.
      valeu! 😀

  2. eu estou levando uma surra com esse LazyInitializationException, inclui o filter sugerido , mas nao mantem aberto , somente se retiro os session.close() do dao.

    1. Olá,
      Nem sempre LazyInitializationException está relacionado somente a session. É preciso ver o stacktrace das exception e vendo o que ele diz. Realmente se vc remover o .close() vai ficar aberto do contrario não. Veja tb onde vc está colocando o seu close().
      flw.

  3. Olá, a minha duvida é agora como ficará um GenericDAO.

    o meu está assim:
    public void incluir(T t) throws Exception {
    Session sessao = HibernateUtil.getSession();
    sessao.beginTransaction();
    sessao.save(t);
    sessao.getTransaction().commit();
    sessao.close();
    }

    Está correto? Devo fechar a sessão ali?

    1. se vc não for mais precisar da sessão após o commit, vc vai e fecha ela. No meu exemplo, na classe que extends a DAO eu gosto de colocar a sessão no finally do try/catch, assim eu sei que ao chegar no finally, já fiz tudo que tinha que fazer na sessão e ela será fechada. No seu caso acima me parece certo.

  4. haha, programadores da madrugada 🙂

    Obrigado, Lopes! Me ajudou bastante. Seu blog é um dos melhores de java.

  5. definitivamente não consegui utilizar o filter com o session.close(); dentro do dao generic
    só dentro do filter

  6. Olá,
    tem como postar um GenericDAO usando as classes acima. Só para matar a dúvida? Tá difícil de usar.

  7. Não me ajudou!!!
    Continua dando erro.
    org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: Classe, no session or session was closed

    1. olá João,
      Então você fez algum ponto errado ou há algo de diferente do seu relacionamento. Mas, essa é uma solução mais comum na internet para resolução do problema, inclusive possuo sistemas antigos em produção usando essa solução.
      flw.

  8. Olá pessoal, do jeito que está o código não commita isso que dizer que ele apenas consulta mas não salva é necessário modificar o método da classe DAO para:
    public static Session getSession() {
    return sessionFactory.getCurrentSession();
    }

Deixe um comentário

O seu endereço de e-mail não será publicado.