Quatro soluções para LazyInitializationException

Olá, tudo bem?

No post de hoje veremos como tratar o famoso erro LazyInitializationException. Vamos ver 4 modos de evitar que esse erro aconteça, a vantagem e a desvantagem de cada abordagem e ao final, veremos comentários sobre o modo de como o EclipseLink trata essa situação.

Para simular e tratar o LazyInitializationException iremos utilizar um projeto EJB 3 com JSF 2.

O que veremos no post de hoje:

  • Página 3: Entenda o problema, por que o erro LazyInitializationException acontece?
  • Página 4: Carregar coleção por anotação
  • Página 5: Carregar coleção por Open Session in View (Transaction View)
  • Página 6: Carregar coleção por Stateful EJB com PersistenceContextType.EXTENDED
  • Página 7: Carregar coleção por Join Query
  • Página 8: Considerações sobre EclipseLink

Ao final do post você irá encontrar o código para download.

Atenção: Para esse post foi utilizado um código bem simples, e que não utiliza padrões de projeto. O foco desse post é destacar as soluções para o LazyInitializationException.

Apesar do exemplo que é utilizado nesse post ter sido criado com JSF + EJB, as soluções aqui se aplicam para o tratamento em qualquer ferramenta web. Tanto JSF, JSP com Servlets, JSP com Struts, JSP com VRaptor, etc.

O único exemplo que não é aplicável para a tecnologia JSE (em geral definido como Desktop) é a solução do EJB.

87 thoughts on “Quatro soluções para LazyInitializationException

  1. Muito bom o post. Eu utilizo join fetch ou Hibernate.initialize(lista). Esse ultimo qdo o provider é o Hibernate. Quando a lista é pequena vai de anotação EAGER mesmo.

    • Olá Thiago, boa tarde.

      Realmente o Hibernate.initialize(lista) é uma boa saída, mas infelizmente não é aplicável à um DAO Genérico. =/

      Caso a aplicação não utilize um DAO Genérico e vá sempre utilizar o Hibernate, esse método é uma boa saída.

  2. Olá Hebert,

    Estou fazendo um tour em seu sítio e estou gostando muito dos artigos. Vou fazer alguns testes com a arquitetura que vc utiliza, pois utilizo aplicação EAR com Glassfish + EJbs + EclipseLink + Jsf2.0 (Primefaces). Quero rever algumas coisas e seus artigos irão ajudar bastante.

    Obrigado pela contribuição.

    • Olá Charles, bom dia.

      Sou suspeito de admitir que sua arquitetura está boa.

      Gosto mundo da combinação de EJB com JSF.

      Agradeço muito seu apoio e espero que você goste dos outros artigos.

      Inté+! o_

      • “Sou suspeito de admitir que sua arquitetura está boa.”

        Pois é, rapaz! Por isso que quero conhecer outras combinações , testar desempenho, liberdade, etc…existem algumas coisas nesta arquitetura que me deixam meio cricri, sabe? Só testando outra(s) para comparar…vou te perguntar bastante coisas, se não for te atrapalhar, claro!

        Mais uma vez, obrigado.

        • Olá Charles, boa tarde.

          Vou tentar ajudar no máximo possível.

          Quando eu estou sem tempo (como agora no momento) eu sempre procuro indicar algo. [=

          Até+ o_

  3. Parabéns pelo post, realmente uma ótima ajuda para muta gente, pois o material sobre o assunto é muito pouco.
    agora caso possa me ajudar. qual seria a melhor solução para uma aplicação que utiliza JSP e Servlets pois ainda não consegui decidir. Obrigado.

    • Olá Fernando, boa tarde.

      Isso vai da sua necessidade e gosto.

      Eu, pessoalmente, sempre optei por trazer todos os dados em uma consulta apenas.

      Não gosto muito do OpenSessionInView pelo fato do N+1. E a solução do EJB nem sempre pode ser aplicada.

      Obrigado pelo apoio.

      • Boa tarde uaihebert.

        eu acabei optando pelo OpenSessionInView por que se eu buscasse todos os dados em uma consulta tava demorando de mais Ex. quando eu busco um estado ele puxava todas as cidades do estado e todas as pessoas da cidade. e quando eu buscava a lista dos estados então.
        O OpenSessionInView foi a melhor forma que encontrei pois não estou usando EJB.

        Mas Obrigado pela atenção.

        • Olá Fernando, boa tarde.

          Quanto ao trazer muita informação do banco de dados em geral é utilizado paginação, mas isso é outra história.

          Fico feliz por você ter conseguido implementar uma solução ao seu sistema.

          Até a próxima.

    • Olá Erick, boa tarde.

      Pretendo editar o post depois para acrescentar outras opções que já encontrei.

      A sua também entrará para a lista.

      Obrigado pela dica.

  4. uaihebert, você saberia dizer se essa estratégia do join query funcionaria com listas? Pelo que andei lendo, essa estrategia funcionaria apenas com sets.

    Muito bom post, me ajudou bastante.

    Abraço.

    • Erick, boa noite.

      Funciona normalmente.
      Bastaria fazer algo do tipo: select p from Person p join fetch p.dogs

      Obrigado pela visita.

  5. Bom dia!
    Estou achei interessante a questão de usar a abertura e fechamento manual da sessao.

    O motivo do comentário é que eu gostaria de pedir uma ajuda sua.

    Eu estou com um problema. Sempre que o datatable (primefaces), carrega as informações do banco os registros são mostrados de forma repetida.

    O objeto que o datatable carrega contém além de tipos básicos (String, Date) tem uma coleção (HashSet) de enum, que no meu caso é um enum de nome de equipamentos (apenas para facilitar a compreensão).

    Se eu coloco apenas um equipamento na coleção:
    Set equipamentos = new HashSet();
    equipamentos.add(Equipamentos.CAMA_HOSPITALAR);

    mostra apenas um registro no datatable. Agora se eu coloco mais de um equipamento, mostra os registros repetidos. Se eu colocar 3 equipamentos, mostra 3 registros e assim por diante.

    Pensei numa solução usando o Criteria para restringir o numero de repetições, mas acho que tem a ver com a forma de consulta do hibernate.

    Tentei ser o mais “abstrato” possível para evitar de mandar o código fonte.
    Sou iniciante no hibernate e gostaria da sua ajuda/opinião.

    Um grande abraço.

    • Rafael, boa tarde.

      O problema não está na consulta. O problema é que você tem 3 registros diferentes como resultado da consulta pois são 3 Equipamentos diferentes que você tem.

      Se o seu problema está que os dados vem duplicados do banco de dados. Você poderia adicionar um distinct na sua consulta ou então colocar o resultado da sua consulta em um Set. Para a solução do Set funcionar corretamente você precisa implementa o equals/hashCode corretamente.

      Caso o seu problema fosse apenas na hora de exibir no datatable você tem diversas soluções. Uma solução seria criar um método que retornasse os 3 equipamentos em uma linha só. Seria algo do tipo:
      public String getEquipamentosAsString(){
      String resultado = "";

      // aqui você adiciona os equipamentos em um alinha só

      return resultado;
      }

      Espero ter ajudado.

  6. Bah Herbert, to usando o OpenSession in View, tenho uma tabela de Doenças e uma outra com os sintomas, a idéia é quando eu clicar na linha da tabela de doenças seja populado os dados no formulário e a tabela de sintomas seja preenchida com os sintomas vinculados aquela doença. Antes de eu paginar minha tabela de doenças tudo funcionava perfeitamente, agora que fiz a paginação estou recebendo esse maldito erro de LazyInitializationException. Minha tabela esta paginada em 5 registros por vez, creio que cada doença não venha ter mais que uns 8 sintomas vinculados a ela. Vc acha muita gambiarra usar o Eager ou sabe me falar que solução posso abordar? Agradeço a ajuda e o post tbm.

    • Pablo, boa noite.

      Pessoalmente eu não gosto de usar EAGER nos atributos, vejo mais desvantagens do que vantagens.

      Eu gosto de utilizar o join fetch. [=

      Até mais.

  7. E quanto a inicialização das listas e/ou objetos lazy diretamente na camada DAO chamando alguma propriedade deles (em uma lista poderia ser um .size() ou então em um objeto um .getId()) ? Você recomenda ?

    • Júnior, boa noite.

      Não recomendo não, e para falar a verdade, vejo isso até como má prática.

      Você estaria disparando uma segunda volta ao banco de dados, sendo que você acabou de disparar uma segunda consulta.

      Imagine que a entidade tenha 3 listas. Aí vai ficar:
      entidade.listaA().size();
      entidade.listaB().size();
      entidade.listaC().size();

      Mais três voltas ao banco de dados.

      Imagine agora que você quer carregar as listas que estariam dentro de um objeto da lista b? um for fazendo .size() em tudo?

      A performance realmente não seria boa.

      Espero ter ajudado.

      • Concordo com você. Mas ao optar pela solução OPEN SESSION IN VIEW não cairia no mesmo cenário ?

        Digo, essas listas iriam como LAZY para a camada VIEW e lá uma query seria disparada novamente ao banco para trazer o objeto.

        • Olá Junior, boa tarde.

          Eu estava querendo dizer que o problema de se fazer .size() em uma lista é que: manualmente esse mesmo código terá que sempre ser adicionado. A cada necessidade de um atributo novo, um novo ‘for’ será realizado via código para trazer essas outras informações.

          O problema do N+1 é sim a desvantagem de OSV ou EJB Stateful. [=

          Infelizmente não existe uma solução que seja 100% boa, eu apenas não indico fazer o .size() por deixar o código mais sujo.

  8. ola, otimo artigo!

    Mas como ficaria com mais de uma lista?

    Tentei assim:

    Query query = entityManager.createQuery(“select p from Person p join fetch p.lazyDogs fetch p.lista2 where p.name = :name”);
    query.setParameter(“name”, name);

    porem da o Erro MultipleBagFetchException

    • Rafael, bom dia.

      É muito comum encontrar essa combinação. [=

      Só deve tomar cuidado com N+1.

      Obrigado pela visita.

    • Rodolpho, boa tarde.

      Fico feliz por ter ajudado.

      Desculpe a demora em responder. Estou muito ocupado nesses últimos dias.

  9. Olá uaihebert, preciso de uma ajuda.
    Há 3 dias estou tentando usar no meu projeto esta solução do OSIV. No meu projeto utilizo EJB3 com JTA.
    Como não deu certo, baixei o código-fonte que você disponibilizou no final dest post. Fiz deploy dele no JBoss7 e, pra minha surpresa, mesmo lá com o filtro habilitado, também deu o erro de LazyException… Help-me!

    • Maycon, boa tarde.

      Ao utilizar EJB você deve utilizar transação manual para realizar corretamente o OSIV.

      O filtro deve estar na camada web e a transação propagada até o EJB.

      Esse post não mostra como fazer tecnicamente isso, mas é isso que você precisa fazer.

      Procure no google sobre como utilizar controle de transação manual com EJB.

      Att,

  10. Hebert, boa tarde.

    Primeiramente, meus parabéns pelo blog. Cara tenho trabalhado muito com listas e uma das maiores dificuldades que encontro é quando carrego num objeto várias listas dentro do meu FETCH, e isso gera um exception (cannot simultaneously fetch multiple bags). Vi diversas soluções em diversas páginas web, mas em sua opinião qual seria a melhor condição para tratar casos assim?

    Abraço

  11. Bom dia,

    Li todos seu livros e tenho todos eles, ótimo trabalho, meus parabéns.

    Tenho um dúvida, antes eu usava o Seam então nunca precisei fazer o Filter para ter o padrão Open Sessin in View, mas estou em um projeto grande com jboss 7, jsf 2, jpa e primefaces, me deparei com o seguinte problema, o filter funcionou legal, so que por exemplo, quando acesso uma pagina para e lista alguns dados e nessa tabela coloco um botão para redirecinar para outra pagina e enviar aquele objeto da linha para outro bean recebo uma exception que não tenho sessão aberta, ainda estou pesquisando, mas se já passou por isso e puder me dar uma ajuda, agradeço.

    Se puder me ajudar posto mais informações sobre o probelma que estou tendo.

    Obrigado, até mais.

    • Danilo, boa tarde.

      Nesse você deve carregar esse objeto todo antes de enviá-lo para a outra página.

      Bastaria fazer uma consulta utilizando join fetch.

      Obrigado pela visita.

    • Olá Anonimo, boa noite.

      Na verdade a paginação é justamente para ajudar com a separação do conteúdo.

      De mais de 1 milhão de views essa é a primeira reclamação sobre a paginação.

      Eu contratei um designer gráfico justamente para não ter problemas de layouts. [=

      Vou ficar atento caso apareça mais reclamações desse tipo.

      Obrigado pela visita. [=

  12. Olá,
    Gostaria de saber como faço o lazy funcionar com a Estratégia “Carregar coleção por Stateless EJB.

    • Olá Av,

      Bastaria fazer como descrito no post.

      Você teria que alterar seu EJB para Stateful e o PersistenceContext.

      Obrigado pela visita.

    • Thiago, bom dia.

      Infelizmente só hoje estou tendo tempo para responder.

      Vi lá no post que você já conseguiu resolver o seu problema.

      Obrigado pela visita.

  13. Meus parabens pelo seu post. Solucionei o meu problema utilizando FetchTyp.Eager. Nao me lembrava que era necessario utilizar esta propriedade. Muito obrigado.

  14. Ola Hebert, curto muito seu blog, ajuda bastante.
    Tentei esta opção para resolver meus problemas com “failed to lazily initialize a collection of role” mas não tive sucesso. :/
    Estou usando o jboss eap 6.2, mudei pra Statefull e ainda o erro…

    • Alysson, boa noite.

      Infelizmente não tenho como te ajudar sem analisar o código.

      Qualquer coisa, poste o código no GUJ que alguém (se não eu) vou ter ajudar.

      Obrigado pela visita.

  15. Hebert,

    Muito bom o blog e o artigo! Parabéns!

    E se no caso eu tiver uma entidade que precisa carregar duas outras entidades ao mesmo tempo.

    Vai dar o problema de org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple

    Como resolver?

    • Fernando, boa noite.

      Primeiro, me perde a demora em responder. Estou muito pegado no trabalho, para você ter idéia, estou te respondendo dele enquanto um deploy não termina.

      Fernando, isso me parece mais algum valor que está ficando em memória após a atualização. Infelizmente eu vejo que isso é mais problema de lógica e não tenho como te ajudar nisso mais profundamente sem uma análise de código. E como podes perceber, estou com -10 em tempo disponível para isso.

      Me desculpe eu não poder ajudar mais.

      Att,

  16. Herbert,

    Tentei a solução Stateful EJB com PersistenceContextType.EXTENDED para o problema do lazy load, com DAO generico. Mas tive um problema na injeção do DAO no Service. Se uso @Inject não resolve o problema e continua dando erro de lazy load, ou seja, a solução não funcionou. Mas quanto troco a injeção para @EJB, injetando o DAO (que é stateful) no Service a solução funciona. Você sabe o por que desta diferença? O @EJB e o @Inject não deveria a principio funcionar igualmente?

    Abraços.

    • Marcelo, boa noite.

      Primeiro, me perde a demora em responder. Estou muito pegado no trabalho, para você ter idéia, estou te respondendo dele enquanto um deploy não termina.

      Honestamente eu não sei te falar sem uma análise melhor. Não sei como seu servidor trata a injeção de EJB por @Inject.

      Desculpe não poder ajudar muito. =/

  17. Olá Hebert,

    Toda vez que tento atualizar algum valor referente à classe B eu recebo o lazyInitializationException.
    Já tentei tornar esse relacionamento EAGER, mas também não funcionou.
    Ah e eu estou conseguindo cadastrar sem problema.

    Você poderia me ajudar ?
    Segue o código abaixo.

    public class A{
    @ManyToMany
    @JoinTable(
    name=”A_B”,
    joinColumns={@JoinColumn(name=”A_id”, referencedColumnName=”id”)},
    inverseJoinColumns={@JoinColumn(name=”B_id”, referencedColumnName=”id”)}
    )
    private List listB;

    }
    public class B{
    @ManyToMany(mappedBy=”ramosAtividade”)
    private List a;
    }

    • Igor, boa tarde.

      Me desculpe a demora, estive muito ocupado nesse último mês.

      Não tem motivo para rolar esse erro mesmo após marcar o relacionamento como EAGER. Eu diriga que algum subrelacionamento pode ainda estar como LAZY.

      Obrigado por tudo e desculpe a demora.

  18. Hebert, boa tarde,

    Tentei implementar a solução com join fetch no entanto o que aconteceu foi que duplicou (ou melhor multiplicou) o resultado, usando seu exemplo, se João possui 3 cachorros, na saída do meu DAO recebi uma lista assim:

    Joao – cachorro1
    Joao – cachorro2
    Joao – cachorro3

    No meu caso estou utilizando o join dessa forma:

    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumns({
    @JoinColumn(name=”coluna1″, referencedColumnName = “coluna1″),
    @JoinColumn(name=”coluna2″, referencedColumnName = “coluna2″),
    @JoinColumn(name=”coluna3″, referencedColumnName = “coluna3″)
    })
    private List lista;

    Estou fazendo alguma coisa errada ?

    Obrigado.

    Um abraço,

    • Olá Edmilson, boa tarde.

      Me desculpe a demora, estive muito ocupado nesse último mês.

      Você está uitlizando o distinct?

      Obrigado por tudo e desculpe a demora.

  19. Eu sempre utilizei essa abordagem “Carregar coleção por Join Query” usando Join Fetch e nunca tive problemas. Onde eu trabalho é utilizado DTOs no controller para evitar o lazyinitializationexception. O que você acha dessa abordagem Hebert?

    Está pergunta se estende a todas as pessoas!!!

Leave a Comment