Spring + Hibernate + Lazy-Load

Pessoal,

Estou com um problema e gostaria de uma ajuda:

Tenho uma classe que possui mapeamento para outra classe via hibernate e utilizo lazy=true.
Possuo no meu web.xml configurado certinho o OpenSessionInViewFilter e funciona tudo correto também. Porém quando eu possuo um objeto que foi acessado em uma request e eu o coloco no contexto da aplicação (Faces), e tento acessar a propriedade lazy em uma outra request acontece o seguinte erro:

11:32:31,984 ERROR LazyInitializationException:19 - could not initialize proxy - the owning Session was closed
org.hibernate.LazyInitializationException: could not initialize proxy - the owning Session was closed
        at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:56)
        at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:98)
        at org.hibernate.proxy.CGLIBLazyInitializer.intercept(CGLIBLazyInitializer.java:158)
        at fConnect.vos.app.CidadeVO$$EnhancerByCGLIB$$c28f4ba7.getUf(<generated>)
        at fConnect.per.dao.com.DAOTipoNotaFiscalImpl.paginacaoPedido(DAOTipoNotaFiscalImpl.java:53)
        at fConnect.srv.com.TipoNotaFiscalSrvImpl.paginacao(TipoNotaFiscalSrvImpl.java:40)
        at fConnect.view.beans.app.BeanGenerico.executaLov(BeanGenerico.java:57)
        at fConnect.view.beans.com.BeanTipoNotaFiscal.actionRealizaConsulta(BeanTipoNotaFiscal.java:63)
        at fConnect.view.beans.com.BeanPedido.actionIniLovTipoNotaFiscal(BeanPedido.java:342)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:585)
        at org.apache.myfaces.el.MethodBindingImpl.invoke(MethodBindingImpl.java:129)
        at org.apache.myfaces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:63)
        at javax.faces.component.UICommand.broadcast(UICommand.java:106)
        at javax.faces.component.UIViewRoot._broadcastForPhase(UIViewRoot.java:94)
        at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:168)
        at org.apache.myfaces.lifecycle.LifecycleImpl.invokeApplication(LifecycleImpl.java:343)
        at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:86)
        at javax.faces.webapp.FacesServlet.service(FacesServlet.java:137)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:237)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:157)
        at OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:157)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:77)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:186)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:157)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:214)
        at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
        at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:198)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:152)
        at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
        at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:118)
        at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:102)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
        at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
        at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:929)
        at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:160)
        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:799)
        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:705)
        at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:577)
        at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:683)
        at java.lang.Thread.run(Thread.java:595)

Este é o mesmo erro que acontecia quando não possuia o OpenSessionInViewFilter porém em todos os lugares que li a única coisa que era necessário fazer é colocar esse filtro no web.xml.

Alguém sabe algo a respeito

Cara esse é um dos erros mais discutidos do Spring. A galera toda o tempo todo está se debatendo em cima do mesmo.

Eu acabei resolvendo o erro criando uma especialização do OpenSessionInViewFilter :

[code]package br.com.cardif.spring.utils;

import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.support.OpenSessionInViewFilter;

/**
*

  • @author Felipe A. Oliveira
    */

public class AutoFlushOpenSessionInViewFilter extends OpenSessionInViewFilter{

/**
 * Busca uma Session do Hibernate e aplica flush mode para auto.
 * @param sessionFactory a SessionFactory
 * @return the Session para utilizar
 * @throws DataAccessResourceFailureException se sessão não for criada
 * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean)
 * @see org.hibernate.FlushMode#AUTO
 */
protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
	Session session = SessionFactoryUtils.getSession(sessionFactory, true);
	session.setFlushMode(FlushMode.AUTO);
	return session;
}

}//the end
[/code]

Depois acrescente no seu Web.xml:


&lt;filter&gt;
		&lt;filter-name&gt;AutoFlushOpenSessionInViewFilter&lt;/filter-name&gt;
		&lt;filter-class&gt;br.com.cardif.spring.utils.AutoFlushOpenSessionInViewFilter&lt;/filter-class&gt;
		&lt;init-param&gt;
			&lt;param-name&gt;singleSession&lt;/param-name&gt;
			&lt;param-value&gt;true&lt;/param-value&gt;
		&lt;/init-param&gt;
	&lt;/filter&gt;
	&lt;filter-mapping&gt;
		&lt;filter-name&gt;AutoFlushOpenSessionInViewFilter&lt;/filter-name&gt;
		&lt;servlet-name&gt;springapp&lt;/servlet-name&gt;
	&lt;/filter-mapping&gt;

Veja se funciona, senão, dê um tok :slight_smile:

Só mais uma coisa, esqueci de mencionar :

this.getHibernateTemplate().merge(model);
this.getHibernateTemplate().flush();

Se tiver usando o HibernateTemplate, como acredito que esteja, faça após o merge (3.0), o flush na mão !!

Cara…

Valeu pela resposta…
Tentei fazer o que você me falou mas não obtive sucesso.
Somente alterei o Filtro para aquele que você me mandou tendo em vista que a única diferença que notei entre ele e o original é que o seu o flushMode da session é AUTO.
Mas referente ao flush “na mão” não vi onde devo aplica-lo, pois até aonde conheço o merge seria como um saveOrUpdate mas o meu problema é com consultas… Ainda não estou nem salvando dados… :frowning:

Mais uma vez obrigado pela ajuda.

[quote=jr_klein]Cara…

Valeu pela resposta…
Tentei fazer o que você me falou mas não obtive sucesso.
Somente alterei o Filtro para aquele que você me mandou tendo em vista que a única diferença que notei entre ele e o original é que o seu o flushMode da session é AUTO.
Mas referente ao flush “na mão” não vi onde devo aplica-lo, pois até aonde conheço o merge seria como um saveOrUpdate mas o meu problema é com consultas… Ainda não estou nem salvando dados… :frowning:

Mais uma vez obrigado pela ajuda.[/quote]

Viajei, acabei não vendo que era acesso.

Dê uma olhadinha nos seus relacionamentos, veja se está tudo correto

pq vc criou uma extensão para o filtro do spring? Eu sempre usei o próprio e nunca tive problemas com lazy. Tem algum outro motivo?

Na extensão somente foi modificado o flushMode…
Agora você me disse que sempre usou o do spring e nunca teve problemas…
Vc já tentou fazer o teste que eu falei??? Vc carrega um objeto em uma request e deixa ele na sessão… ai em outra request vc tenta capturar uma propriendade que é lazy e nunca havia sido carreada…

Esse é o problema que estou enfrentando… ainda ñ consegui resolve-lo…

Vlw

jr_klein eu não sei se entendi direito mas acho que o problema é que você fez o load do objeto e o colocou na sessão, e depois em outro request você tentou acessar o objeto e deu o problema do lazy. Se for isso acho que sei o que ocorreu, na verdade o OpenSessionViewFilter vai fechar a session após a criação do response a cada request, ou seja, quando a página jsp foi compilada e criada, como você não deve ter acessado a propriedade lazy do objeto antes desse momento o hibernate não realizou a busca das informações então ao acessar a propriedade lazy do objeto da sessão o proxy para buscar as informações vai dar problema porque a session do hibernate já foi fechada anteriormente… para a solução acesse a propriedade após carregar o objeto… só dar um get ou varrer a lista… acho que não fui muito claro mais fica ai a ajuda…

HUmmm, nunca tinha parado para pensar nisso.

Se você acessar a propriedade lazy na mesma requisição onde você obteve o objeto, esta propriedade já ficará carrega no seu objeto, ou não? Aí você conseguiria acessar em outra request. Me refiro a algo do tipo:

  //action buscar cliente
  Cliente cliente = obtemCliete(id);
  cliente.getCategory();
  session.setAttribute("cliente", cliente);

Fazendo isso, como a categoria já teria sido carregada, poderia ser acessada em outra request.

Será que isso rola? Dá certo? Se bem que me parece uma solução meio porca…

[quote=carneiro]HUmmm, nunca tinha parado para pensar nisso.

Se você acessar a propriedade lazy na mesma requisição onde você obteve o objeto, esta propriedade já ficará carrega no seu objeto, ou não? Aí você conseguiria acessar em outra request. Me refiro a algo do tipo:

  //action buscar cliente
  Cliente cliente = obtemCliete(id);
  cliente.getCategory();
  session.setAttribute("cliente", cliente);

Fazendo isso, como a categoria já teria sido carregada, poderia ser acessada em outra request.

Será que isso rola? Dá certo? Se bem que me parece uma solução meio porca…[/quote]

Seta o Lazy para false, vai resolver seu problema. Só tome cuidado com o tamanho do objeto para não onerar sua performance.

[]´s

tem razão… viajei

Galera…
Eu não posso implementar as idéias q vocês deram como sugestão pois meus objetos podem disparar vários selects que não seriam necessários…
Tive que fazer uma “gambiarra” para quando ele tente acessar uma propriedade Lazy a sessão estiver fechada eu busco a nova sessão criada pelo OpenSessionInViewFilter…
Se alguém tiver alguma solução mais “elegante” aceito sugestões…
Brigadão

Olah!
Nao sei se alguem vai responder, mas eu gostaria de entender essa “gambiarra” para tentar aplica-la, pois estou passando pelo mesmo problema e ainda nao consegui soluciona-lo.

Poderia postar o codigo ou algo do tipo?

Obrigado!

tb estou com esta duvida, tem alguma forma de um objeto com lazy ficar na sessão do jsf e ser acessado por mais de um request ?

[quote=jr_klein]Galera…
Eu não posso implementar as idéias q vocês deram como sugestão pois meus objetos podem disparar vários selects que não seriam necessários…
Tive que fazer uma “gambiarra” para quando ele tente acessar uma propriedade Lazy a sessão estiver fechada eu busco a nova sessão criada pelo OpenSessionInViewFilter…
Se alguém tiver alguma solução mais “elegante” aceito sugestões…
Brigadão
[/quote]

Voce encontrou uma solução mais elegante para isso???