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.