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.