hibernate: LazyInitializationException: não foi possível inicializar o proxy

Aqui está uma que me deixou perplexo. Eu estou tentando implementar uma estrutura básica do DAO do Hibernate, mas estou tendo um problema.

Aqui está o código essencial:

int startingCount = sfdao.count(); sfdao.create( sf ); SecurityFiling sf2 = sfdao.read( sf.getId() ); sfdao.delete( sf ); int endingCount = sfdao.count(); assertTrue( startingCount == endingCount ); assertTrue( sf.getId().longValue() == sf2.getId().longValue() ); assertTrue( sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() ) ); assertTrue( sf.getSfTransactionNumber().equals( sf2.getSfTransactionNumber() ) ); 

Ele falha no terceiro assertTrue onde está tentando comparar um valor em sf com o valor correspondente em sf2. Aqui está a exceção:

 org.hibernate.LazyInitializationException: could not initialize proxy - no Session at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:86) at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:140) at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190) at com.freightgate.domain.SecurityFiling_$$_javassist_7.getSfSubmissionType(SecurityFiling_$$_javassist_7.java) at com.freightgate.dao.SecurityFilingTest.test(SecurityFilingTest.java:73) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:40) 

O problema é que você está tentando acessar uma coleção em um object que é desanexado . Você precisa append novamente o object antes de acessar a coleção para a session atual. Você pode fazer isso através

 session.update(object); 

Usar lazy=false não é uma boa solução porque você está jogando fora o recurso de Inicialização Preguiçosa do modo de hibernação. Quando lazy=false , a coleção é carregada na memory ao mesmo tempo em que o object é solicitado. Isso significa que, se tivermos uma coleção com 1000 itens, todos eles serão carregados na memory, apesar de acessá-los ou não. E isso não é bom.

Por favor, leia este artigo onde explica o problema, as possíveis soluções e porque é implementado desta forma. Além disso, para entender Sessions and Transactions, você deve ler este outro artigo .

Isso geralmente significa que a session do Hibernate já foi fechada. Você pode fazer um dos seguintes para consertá-lo:

  1. qualquer object que crie este problema, use HibernateTemplate.initialize(object name)
  2. Use lazy=false em seus arquivos hbm.

Veja meu artigo. Eu tive o mesmo problema – LazyInitializationException – e aqui está a resposta que eu finalmente encontrei:
http://community.jboss.org/wiki/LazyInitializationExceptionovercome
Definir preguiça = falso não é a resposta – pode carregar tudo de uma vez, e isso não é necessariamente bom. Exemplo:
1 tabela de registros A referências:
5 referências de tabela B de registros:
25 registros de referências da tabela C:
Tabela de 125 registros D

Este é apenas um exemplo do que pode dar errado.
– Tim Sabin

Se você estiver usando o hibernate com annotations JPA, isso será útil. Em sua class de serviço, deve haver um setter para o gerenciador de entidades com @PersistenceContext. mude para @PersistenceContext (type = PersistenceContextType.EXTENDED). Então você pode acessar a propriedade preguiçosa em qualquer lugar.

Se você estiver usando Lazy loading, seu método deve ser anotado com

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) para EJB de session sem estado

Encontramos esse erro também. O que fizemos para resolver o problema é que adicionamos um preguiçoso = falso no arquivo de mapeamento do Hibernate.

Parece que tivemos uma class A dentro de uma Session que carrega outra class B. Estamos tentando acessar os dados na class B, mas esta class B é separada da session.

Para podermos acessar esta Classe B, tivemos que especificar no arquivo de mapeamento do Hibernate da class A o atributo lazy = false. Por exemplo,

     

Ok, finalmente descobri onde eu estava negligente. Eu estava com a noção errada de que deveria envolver cada método DAO em uma transação. Terrivelmente errado! Eu aprendi minha lição. Eu transportei todo o código de transação de todos os methods DAO e configurei transactions estritamente na camada de aplicativo / gerenciador. Isso resolveu totalmente todos os meus problemas. Os dados são carregados com preguiça quando preciso, embrulhados e fechados quando faço o commit.

A vida é boa … 🙂

Se você souber sobre o impacto de lazy=false e ainda quiser torná-lo como padrão (por exemplo, para fins de prototipagem), você pode usar qualquer um dos seguintes:

  • se você estiver usando configuração XML: adicione default-lazy="false" ao seu elemento
  • se você estiver usando configuração de anotação: adicione @Proxy(lazy=false) em sua (s) class (s) de entidade

Parece que apenas o seu DAO está usando a session. Assim, uma nova session é aberta e, em seguida, fecha para cada chamada para um método DAO. Assim, a execução do programa pode ser retomada como:

 // open a session, get the number of entity and close the session int startingCount = sfdao.count(); // open a session, create a new entity and close the session sfdao.create( sf ); // open a session, read an entity and close the session SecurityFiling sf2 = sfdao.read( sf.getId() ); // open a session, delete an entity and close the session sfdao.delete( sf ); etc... 

Por padrão, a coleta e a associação em uma entidade são preguiçosas: elas são carregadas do database sob demanda. Portanto:

sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() )

está lançando uma exceção porque solicita um novo carregamento do database e a session associada ao carregamento da entidade já foi fechada.

Existem duas abordagens para resolver esse problema:

  • crie uma session para include todo o nosso código. Assim, isso significaria alterar seu conteúdo do DAO para evitar a abertura de uma segunda session

  • crie uma session e atualize (ou seja, reconecte) sua entidade para esta session antes das asserções.

    session.update (object);

Se você está gerenciando a session do Hibernate manualmente, você pode querer olhar para sessionFactory.getCurrentSession () e documentos associados aqui:

http://www.hibernate.org/hib_docs/v3/reference/en/html/architecture-current-session.html

Eu acho que Piko significa em sua resposta que existe o arquivo hbm. Eu tenho um arquivo chamado Tax.java. As informações de mapeamento são salvas no arquivo hbm (= mapeamento de hibernação). Na tag de class, há uma propriedade chamada lazy . Defina essa propriedade como true. O seguinte exemplo de hbm mostra uma maneira de definir a propriedade lazy como false .

id …

Se você estiver usando Anotações, procure na documentação de hibernação. http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/

Espero que tenha ajudado.

use o Hibernate.initialize para o campo preguiçoso

Se você estiver usando a anotação Spring e JPA, a maneira mais simples de evitar problemas com a session na boot lenta é reproduzir novamente:

 @PersistenceContext 

para

 @PersistenceContext(type = PersistenceContextType.EXTENDED) 

Por padrão, todas as associações one-to-many e many-to-many são obtidas preguiçosamente ao serem acessadas pela primeira vez.

No seu caso de uso, você poderia superar esse problema agrupando todas as operações do DAO em uma transação lógica:

 transactionTemplate.execute(new TransactionCallback() { @Override public Void doInTransaction(TransactionStatus transactionStatus) { int startingCount = sfdao.count(); sfdao.create( sf ); SecurityFiling sf2 = sfdao.read( sf.getId() ); sfdao.delete( sf ); int endingCount = sfdao.count(); assertTrue( startingCount == endingCount ); assertTrue( sf.getId().longValue() == sf2.getId().longValue() ); assertTrue( sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() ) ); assertTrue( sf.getSfTransactionNumber().equals( sf2.getSfTransactionNumber() ) ); return null; } }); 

Outra opção é buscar todas as associações LAZY ao carregar sua entidade, para que:

 SecurityFiling sf2 = sfdao.read( sf.getId() ); 

deve buscar também o tipo de submissionType LAZY:

 select sf from SecurityFiling sf left join fetch.sf.submissionType 

Dessa forma, você busca ansiosamente todas as propriedades preguiçosas e pode acessá-las depois que a Sessão for fechada também.

Você pode buscar tantas associações [one|many]-to-one e uma “[um | muitos] -para muitos” associações de lista (por causa da execução de um produto cartesiano).

Para inicializar vários “[um | muitos] -para muitos”, você deve usar o Hibernate.initialize (coleção) , logo após carregar sua entidade raiz.