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.
-
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.
-
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() {} }
-
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.
-
Abra seu arquivo hibernate.cfg.xml e adicione a linha abaixo:
< property name=”hibernate.current_session_context_class”>thread < /property>
-
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.
-
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.
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! 😀
eu estou levando uma surra com esse LazyInitializationException, inclui o filter sugerido , mas nao mantem aberto , somente se retiro os session.close() do dao.
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.
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?
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.
HibernateUtil no meu caso é o DAO do seu exemplo.
ninguem dorme por aqui .. !!! é so java no corujão!
eu sofro de insônia hehe :).
haha, programadores da madrugada 🙂
Obrigado, Lopes! Me ajudou bastante. Seu blog é um dos melhores de java.
definitivamente não consegui utilizar o filter com o session.close(); dentro do dao generic
só dentro do filter
Olá,
tem como postar um GenericDAO usando as classes acima. Só para matar a dúvida? Tá difícil de usar.
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
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.
Olá Sergio, obrigado por compartilhar, você pode posta a classe SessionFactory? obrigado
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();
}
agora sim vai funcionar 🙂