2008-12-06 12 views
67

Oto jeden, który mnie wprawia w zakłopotanie. Próbuję zaimplementować podstawową strukturę DAO Hibernate, ale mam problem.hibernacja: LazyInitializationException: nie można zainicjować proxy

Oto niezbędne kod:

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())); 

To nie na trzecim assertTrue gdzie próbuje porównać wartość w SF do odpowiedniej wartości w SF2. Oto wyjątek:

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) 

Odpowiedz

15

Zasadniczo oznacza to, że posiadana sesja hibernacji została już zamknięta. można wykonać jedną z następujących czynności, aby je naprawić:

  1. dowolny przedmiot tworząc ten problem, użyj HibernateTemplate.initialize(object name)
  2. Korzystając lazy=false w plikach HBM.
+0

miał ten sam problem i leniwy = false naprawił . Dzięki – autonomatt

+1

teraz w moim przypadku używam 'lazy = false' dla wszystkich poziomów dao, ale okazuje się, że wydajność aplikacji jest powolna z tego powodu, próbowałem ustawić' lazy = true', ale teraz wywoływany jest lazy wyjątek, wszelkie sugestie jak to może być naprawiony. – Rachel

+0

pakore, czy możesz wskazać, dlaczego nie jest rozwiązaniem i jak to zrozumieć? – Victor

2

porządku, w końcu zorientowali się, gdzie byłem niedbały. Byłem w błędnym przekonaniu, że powinienem zawijać każdą metodę DAO w transakcji. Strasznie nie tak! Nauczyłem się mojej lekcji. Wciągnąłem cały kod transakcji ze wszystkich metod DAO i skonfigurowałem transakcje wyłącznie na warstwie application/manager. To całkowicie rozwiązało wszystkie moje problemy. Dane są odpowiednio leniwy, ponieważ jest to potrzebne, zawinięte i zamknięte po wykonaniu zatwierdzenia.

Życie jest spora ... :)

+0

Nie jestem pewien, czy w pełni rozumiem, ponieważ nie pamiętam, aby widziałem to w innych projektach. Ale masz rację: dodanie "@ org.springframework.transaction.annotation.Transactional (readOnly = true)" do metod w warstwie serwisowej rozwiązało problem. (W tej warstwie pobierzemy obiekt i przekazujemy go innemu wywoływaniu do DAO.) – Arjan

1

myślę Piko oznacza w swojej odpowiedzi, że istnieje plik HBM. Mam plik o nazwie Tax.java. Informacje mapowania są zapisywane w pliku mapowania hbm (= hibernacji). W znaczniku klasy znajduje się właściwość o nazwie leniwy. Ustaw tę właściwość na true. Poniższy przykład hbm pokazuje sposób ustawienia leniwej właściwości na false.

` id ...”

Jeśli używasz adnotacji zamiast spojrzeć w documenation hibernacji. http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/

Mam nadzieję, że pomogło.

4

jeśli używasz leniwy załadunku metodę muszą być opatrzone

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) dla Stateless Session EJB

3

wystąpił ten błąd, jak również. Aby rozwiązać ten problem, dodaliśmy plik lazy = false w pliku odwzorowania hibernacji.

Wygląda na to, że mieliśmy klasę A znajdującą się wewnątrz sesji, która ładuje inną klasę B. Próbujemy uzyskać dostęp do danych klasy B, ale ta klasa B jest odłączona od sesji.

Abyśmy mogli uzyskać dostęp do tej klasy B, musieliśmy określić w pliku odwzorowania hibernacji klasy A atrybut lazy = false. Na przykład,

 <many-to-one name="classA" 
       class="classB" 
       lazy="false"> 
     <column name="classb_id" 
       sql-type="bigint(10)" 
       not-null="true"/> 
    </many-to-one> 
68

Problemem jest to, że próbujesz uzyskać dostęp do kolekcji w obiekcie, który jest detached. Musisz ponownie dołączyć obiekt przed uzyskaniem dostępu do kolekcji do bieżącej sesji. Można to zrobić poprzez

session.update(object); 

Korzystanie lazy=false nie jest dobrym rozwiązaniem, ponieważ jesteś wyrzucać funkcję leniwe inicjowanie z hibernacji. Gdy lazy=false, kolekcja jest ładowana do pamięci w tym samym czasie, gdy żądany jest obiekt. Oznacza to, że jeśli mamy kolekcję zawierającą 1000 pozycji, wszystkie zostaną załadowane do pamięci, mimo że będziemy mieli do nich dostęp. A to nie jest dobre.

Przeczytaj ten numer article, w którym wyjaśniono problem, możliwe rozwiązania i dlaczego został on wdrożony w ten sposób. Ponadto, aby zrozumieć Sesje i Transakcje, należy przeczytać this other article.

7

Jeśli używasz hibernacji z adnotacjami JPA, będzie to przydatne. W twojej klasie usług powinien być ustawiciel dla menedżera encji z @PersistenceContext. zmień to na @PersistenceContext (type = PersistenceContextType.EXTENDED). Następnie możesz uzyskać dostęp do leniwej nieruchomości w dowolnym miejscu.

+1

To nie jest poprawne, chyba że ręcznie zarządzasz swoimi transakcjami. Typ kontekstu upakowania Spring EXTENDED dotyczy długiego wzorca konwersacji, a nie wzoru sesji na żądanie, o który pyta OP. – HDave

+0

Chyba @HDave ma rację; zobacz także [Jaka jest różnica między kontekstem trwałości transakcji a kontekstem rozszerzonej trwałości?] (http://stackoverflow.com/questions/2547817/what-is-the-difference-between-transaction-scoped- servistence-context- i-extend) – Arjan

2

Jeśli wiesz o wpływie lazy=false i nadal chcą czyni je jako domyślne (np dla celów prototypowania), można użyć dowolnej z następujących czynności:

  • jeśli używasz konfiguracji XML: dodaj default-lazy="false" do <hibernate-mapping> elementu
  • jeśli używasz konfiguracji Uwagi: Dodawaj @Proxy(lazy=false) do klasy podmiotu (ów)
11

patrz mój artykuł. Miałem ten sam problem - LazyInitializationException - i oto odpowiedź, którą w końcu wymyśliłem:
http://community.jboss.org/wiki/LazyInitializationExceptionovercome
Ustawienie lazy = false nie jest odpowiedzią - może załadować wszystko na raz, a to niekoniecznie jest dobre. Przykład:
1 rekord odniesienia Tabela A:
5 Rejestry odnośniki tabela B:
25 Rejestry referencje Tabela C:
125 rekordy tabeli D
...
itd. To jest tylko jeden przykład tego, co może pójść źle.
--Tim Sabin

+0

Powinieneś wyjaśnić tutaj rozwiązanie, a nie link do strony trzeciej. –

2

Wydaje się tylko Twój DAO używasz sesji. Tak więc nowa sesja jest otwarta, a następnie zamknięta dla każdego wywołania metody DAO. Zatem realizacja programu może zostać wznowione jak:

// 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... 

Domyślnie, gromadzenia się i stowarzyszania się w jednostce są leniwi: są one ładowane z bazy danych na żądanie.Zatem:

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

rzuca wyjątek, ponieważ zażądać nowego załadunek z bazy danych, a sesja związana z załadunkiem podmiotu została już zamknięta.

Istnieją dwa sposoby rozwiązania tego problemu:

  • utworzyć sesji na załączonym całego naszego kodu. Oznacza to zmianę zawartości DAO, aby uniknąć otwarcia drugiej sesji, a następnie utworzyć sesję, a następnie zaktualizować (tzn. Ponownie połączyć) swój obiekt z tą sesją przed asercją.

    session.update (obiekt);

0

Domyślnie wszystkie one-to-many i many-to-many stowarzyszenia są pobierane leniwie na uzyskiwany po raz pierwszy.

W przypadku użycia, można rozwiązać ten problem przez zawijania wszystkie operacje DAO w jedną logiczną transakcji:

transactionTemplate.execute(new TransactionCallback<Void>() { 
    @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; 
    } 
}); 

Innym rozwiązaniem jest, aby pobrać wszystkie leniwy skojarzenia po załadowaniu swoją jednostkę, tak, aby:

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

należy pobrać leniwy submissionType też:

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

W ten sposób z przyjemnością pobierasz wszystkie leniwe właściwości i będziesz mieć do nich dostęp po zamknięciu sesji.

Możesz pobrać tyle asocjacji [one|many]-to-one i jedno powiązanie "[jeden | wiele] do wielu" (z powodu działania produktu kartezjańskiego).

Aby zainicjować wiele "[jeden | wiele] do wielu", należy użyć Hibernate.initialize(collection), zaraz po załadowaniu encji głównej.

1

Jeśli używasz Spring i JPA adnotacji, najprostszy sposób na uniknięcie problemu z sesji w leniwe initialize jest replaysing:

@PersistenceContext 

do

@PersistenceContext(type = PersistenceContextType.EXTENDED) 
+1

Działa to tylko wtedy, gdy zarządzasz ręcznie swoimi transakcjami – Gemasoft