Galera estou trabalhando em projeto JSF e utilizo EJB, JPA/Hibernate e toda vez que tento acessar um List nas minhas Entitys da o seguinte erro:
>Exception occurred in target VM: failed to lazily initialize a collection of role: persistence.bean.Pessoa.emailList, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: persistence.bean.Pessoa.emailList, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119)
at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248)
at manager.MBEmail.recuperarSenhaEmailPrincipal(MBEmail.java:48)
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:597)
at com.sun.el.parser.AstValue.invoke(AstValue.java:234)
at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:297)
at org.jboss.weld.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:43)
at org.jboss.weld.el.WeldMethodExpression.invoke(WeldMethodExpression.java:72)
at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:98)
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:88)
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
at javax.faces.component.UICommand.broadcast(UICommand.java:315)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:775)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1267)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:82)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:312)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1523)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:343)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:215)
at filtro.FiltroLogin.doFilter(FiltroLogin.java:121)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:215)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:277)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:188)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:641)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97)
at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:85)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:185)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:332)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:233)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:165)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:791)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:693)
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:954)
at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:170)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
at com.sun.grizzly.ContextTask.run(ContextTask.java:69)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309)
at java.lang.Thread.run(Thread.java:619)
<
Uma das maneiras de você corrigir esse problema é carregar novamente a lista de emails por pessoa. exemplo:
Na camada DAO você pode criar um método assim:
public List<Email> listEmailPorPessoa(Integer idPessoa) {
Query q = getEntityManager().createQuery("SELECT e FROM Email e WHERE e.pessoa.id = idPessoa");
q.setParameter("idPessoa", idPessoa);
return q.getResultList();
}
pessoa.setEmailList(new PessoaDAO().listEmailPorPessoa(pessoa.getId()));
1 curtida
Galera realizando testes descobri o seguinte:
Considerando as seguintes Entitys Class:
Pessoa (Pessoa possui uma lista de Email e Endereços)
Email
Endereco
Usando Set<>
1° Se eu uso Set<> com fetch=FetchType.EAGER
nos mapeamentos funciona! Os seja, o metodo find do EntityManager traz os Sets preenchidos!
2° Se eu uso Set<> com fetch=FetchType.LAZY
nos mapeamentos o método find do EntityManager retorna o seguinte erro:
- Quando utilizo Set mesmo sem fazer
fetch=FetchType.EAGER
consigo fazer o seguinte jp ql:
select p from Pessoa p
join fetch p.emailSet em
join fetch p.enderecoSet en
where p.pesId = 1
=====================================================================================
Usando List<>
1° Se eu uso List<> com fetch=FetchType.EAGER
nos mapeamentos o método find do EntityManager retorna o seguinte erro:
2° Se eu uso List<> com fetch=FetchType.LAZY
nos mapeamentos o método find do EntityManager retorna o seguinte erro:
- Quando utilizo List de nenhuma maneira consigo fazer o seguinte jp ql:
select p from Pessoa p
join fetch p.emailList em
join fetch p.enderecoList en
where p.pesId = 1
pois tenho o seguinte erro:
- Utilizando List eu consigo fazer o seguinte jp ql e funciona!
Só não consigo entender porque funciona com Set e não Funciona com List se alguem puder me explicar fico grato e grupo agradece!
Este problema ocorre quando a transacao/sessao encontra-se fechada. Neste caso, em um relacionamento com fetchType Lazy, os agregados nao sao carregados quando a query eh executada em cima do “pai”. Isso nao ocorre com fetchType EAGER, pois com este tipo de relacionamento tanto o “pai” como os “filhos”, sao retornados mapeados na entidade.
Observar que esse tipo de mapeamento (EAGER) nao eh recomendado para agregados do tipo @OneToMany, pois isso poderia prejudir e muito a performance do banco de dados.
Somente para reforcar, como voce esta trabalhando com EJB, assumo que o tipo da transacao configurada no seu persistence.xml eh to tipo JTA (CMT). Bom, isso quer dizer que, toda vez que voce faz um simples consumo de um EJB, invocando seu metodo, um interceptador eh acionado para abrir uma transacao antes mesmo de entrar no metodo. Se nenhuma Runtime exception for lancada, no termino do metodo este mesmo interceptador comita a transacao e o processo eh entao finalizado. Seguindo este racioncionio, quando voce faz acesso ao banco de dados atravez de um EJB utilizando a API do JPA, no termino dessa consulta, quando vc estiver na view, a sessao estara fechada e portanto, o erro em questao ira explodir caso voce tente realizar de forma Lazy, acessos aos agregados da entidade. Isso tambem eh comum quando vc tenta renderizar na pagina, o resultado da pesquisa ao realizar bind nos JSPs de atributos mapeados com a entidade que esta fazendo a pesquisa.
Espero ter tirado sua duvida
Abracos
alexmdo sua explicação foi perfeita! Consegui entender, e realmente estou usando JTA. Usar EAGER nos mapeamentos @OneToMany não é bom devido a
carga realizada no banco de dados certo! Qual seria a maneira correta de se trabalhar com esses mapeamentos haja visto que preciso dos dados que estão
associados a ele? usar multiple bags em uma consulta jp ql? Ex:
select p from Pessoa p
join fetch p.emailSet em
join fetch p.enderecoSet en
where p.pesId = 1
tente forçar o DAO a carregar a list 
Forçar o DAO eu posso fazer de duas formas usando EAGER ou fazendo multiples bags em jp ql! Qual sera a forma correta?
Entao josimarsis. Não existe uma maneira correta. Contanto que você consiga trazer essas informações preenchidas antes da sessão fechar, já fica valendo.
A estratégia que você exemplificou, acredito que funcionaria. Já é uma solução!
Outra forma voce também poderia pensar em fazer.
Por exemplo:
- Criar DTOs como retorno dos seus metodos para devolver para a view, as informações que você quer que seja renderizado (não curto muito essa solução, mas funciona);
- Utilizar persistencia extendida através dos Stateful Session Bean (este, poderia de repente ser um DAO generico, sei la. Nao validei essa ideia);
- Utilizar o conceito de OpenSessionInView - Para cada requisicao, uma transacao é aberta. No render response, a transacao é comitada caso nenhuma Runtime Exception seja lançada;
Enfim, existe N alternativas. O que for melhor pra você e funcionar, fica sendo a escolha correta.
Eu só não consigo entender por que Set aceita multiplos bags e List não aceita, já pesquisei mais não achei nada a respeito!
Alguém poderia me responder? Existe alguma forma de o List aceitar multiplos bags? Isso seria um bug
Olá!
O fato de poder utilizar soment um Fetch como EAGGER é uma limitação do JPA.
O motivo pelo qual isso acontece é que ele faz um JOIN nas tabelas que estão relacionadas como EAGGER.
Vamos ver um exemplo simples:
Pessoa:
ID_PESSOA NOME
1…Joao
Compra
ID_COMPRA ID_PESSOA PRECO
1…1…100,00
2…1…150,00
ProfissaoPessoa (não estou colocando a tabela profissão pra simplificar, mas 1 é programador, por exemplo)
ID_PROFISSAO ID_PESSOA
5…1
Um relacionamento EAGGER
ID_PESSOA NOME ID_COMPRA ID_PESSOA ID_PESSOA ID_COMPRA PRECO
1…Joao…1…1…1…1…100,00
1…Joao…1…2…2…1…100,00
Dois relacionamentos EAGGER
ID_PESSOA NOME ID_COMPRA ID_PESSOA ID_PESSOA ID_COMPRA PRECO ID_PROFISSAO ID_CLIENTE ID_CLIENTE ID_PROFISSAO
1…Joao…1…1…1…1…100,00…5…1…1…5
1…Joao…1…1…1…1…100,00…5…1…1…5
Então com EAGGER acredito que você não conseguirá utilizar relacionar mais de um Bag.
Sobre o Set, eu não sei como ele funciona.
Sendo assim como resolver este problema pois em varias ocasiões necessito fazer join fetch?
Existe uma alternativa?
Sei que o tópico é antigo, porém é extremamente fácil encontrá-lo no Google, então para evitar problemas futuros façam o seguinte.
Os Mapeamentos que usam lista (e lista dentro de lista) dão dois problemas clássicos: multiple bags ou o failed to lazily inicitialize.
A solução (que eu estudei) mais comum na Internet é a alteração de List para Set.
Porém acredito eu, em minha humilde opinião, não ser a mais elegante.
Para mapear uma lista comumente utilizo:
@ManyToMany(cascade = CascadeType.REFRESH, fetch = FetchType.EAGER)
@Fetch(FetchMode.SUBSELECT)
private List<Classe> objetos;
O Fetch é encontrado em org.hibernate.annotations.Fetch.
E o ManyToMany em javax.persistence.ManyToMany.
Comumente esta formatação é válida para @OneToMany também.
Apliquem essa Annotation sobre as listas mapeadas que provavelmente o Hibernate operará com sucesso as transações.
Caso tenha falado alguma besteira me desculpem, infelizmente tenho pouca experiência em Java.
Att.
2 curtidas
Com a solução do perotto consegui resolver o problema.
Você sabe dizer o que o @FetchMode.SUBSELECT faz?
kayanbl não sei efetivamente o que o SUBSELECT faz, a impressão que tenho é que o SUBSELECT faz uma “segunda” consulta além da qual foi solicitada para carregar e vincular ao objeto a coleção (lista) desejada.
Uso a bastante tempo e particularmente funciona bem.
Caso tenha prestado alguma informação equivocada por favor me corrijam.
Att.
Mas o tal do EAGER não é má prática? Pensa num mapeamento de uma classe Cliente, como uma lista de vendas efetuadas para ele. Caso eu queira apenas o NOME do cliente, quando eu recuperá-lo, se eu tiver o fetch eager, o sistema vai ir buscar na base de dados não apenas seu nome como todas as vendas já efetuadas para ele. Num universo em que o cliente pode ter váááááárias vendas, a coisa tende a ficar super lenta, certo?
Já com o fetch LAZY as vendas só seriam carregadas quando fosse chamado explicitamente o metodo getVendas(). E é aí que o bicho tá pegando. O sistema está indo na base, pega os dados principais do cliente e fecha a sessao. Depois disso, quando se faz o getVendas, está dando erro de que a sessao está fechada.
Pelo que estou vendo com os posts aí de cima, a melhor maneira para a gente não se incomodar é carregar a lista “no muque”, quando elas forem necessárias, via algum método do BO.
Posso estar equivocado, claro, mas me parece que é melhor colocar essa lista de itens na entidade sem anotá-la como @onetomany e declarada como @transient. Se tu vai ter que, de qualquer forma, carregá-la separadamente, quando necessário, não vejo lucro em ter de ficar me preocupando com os mapeamentos.
O que parece aos amigos?
Abraço a todos
Giovanni
Da uma pesquisada sobre open session in view. Ou use Criteria ou SQL nativo mesmo caso a query for complexa.
http://www.guj.com.br/java/205965-open-session-in-view-com-jpa
http://uaihebert.com/?p=1367&page=5