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:
HibernateTemplate.initialize(object name)
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:
default-lazy="false"
ao seu elemento
@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.