LazyInitializationException mit Hibernate

PHANTOMIAS

Aktives Mitglied
Hallo an alle!

Ich erhalte folgende Meldung beim Laufen meines Tests:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.mydomain.myproject.data.domain.User.groups, 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.initialize(AbstractPersistentCollection.java:365)
at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:108)
at org.hibernate.collection.PersistentBag.iterator(PersistentBag.java:272)
at com.mydomain.myproject.data.services.PersonServiceImpl.updatePerson(UserServiceImpl.java:45)
at com.mydomain.testing.UserTest.testUpdateUser(UserTest.java:127)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:24)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Dies scheint ein häufiges Problem zu sein, leider konnte ich bisher keine Lösung für mich finden.

Aber nun mal zur Erklärung: Ich habe hier eine n:m Relation und zwischen Group und User.
Mit Hibernate habe ich das so notiert um eine bidirektionale manytomany Verbindung zu erreichen:
Java:
User.java

@ManyToMany(mappedBy="users")
private List<Group> groups;

Java:
Group.java

@ManyToMany
@JoinTable(name = "Group_User",
	joinColumns = {@JoinColumn(name="group_id", referencedColumnName="id")},
	inverseJoinColumns = {@JoinColumn(name="user_id", referencedColumnName="id")}
)
private List<User> users;

Das Problem kann gelöst werden in dem ich den FetchType auf EAGER setze (manytomany ist ja standardmässig LAZY). Aber das will ich nicht, da ich im Frontend (Flex) eine Art Rekursion erhalte. Also ich bekomme wenn ich alle Gruppen lese, dann natürlich auch gleich alle User mit. Und die User kriegen wieder alle Gruppe, die wieder alle User, ... Ich setze also recht viele SQL-Abfragen ab und das hier ist nur ein Ausschnitt meiner Datenbank-Struktur. Im Prinzip habe ich alles EAGER gemacht um im Frontend das zu umgehen, dass ich da Libs wie Gilead einbinden muss, welche damit dann "umgehen" können, da BlazeDS als Adapter das von Hause aus nicht kann.

Also beginne ich zur Zeit das ohne EAGER allein mal auf Java-Seite zu regeln.

Was ich habe und wo der Fehler genau geworfen wird, folgt nun:
Hier habe ich die Testklasse, deren Methode aufgerufen wird:

Code:
UserTest.java

@Test
public void testUpdateUser() {
	List<User> users = service.getAllUsers();
	User user = users.get(0);

	// change & save user
	user.setEmail("updateMail@test.com");

	// HERE IS THE ERROR TRACE
	service.updateUser(user);

	// ...
}
Bei updateUser(user) geht es in die Service-Implementierung, bei der dann in der Zeiel des Iterators dann der Fehler "auftritt".
Java:
UserServiceImpl.java

public void updateUser(User u) {
	List<Group> groups = u.getGroups();

	// THIS LINE ERROR TRACE
	Iterator<Group> iterator = groups.iterator();
	while (iterator.hasNext()) {
		Group group = iterator.next();
		// ...
	}
	// ...
}
Wie kann ich das nun lösen ohne die Relation auf EAGER setzen zu müssen?
Ich nutze neben Hibernate noch Spring 3.0.3., muss aber gestehen, dass ich noch Anfänger bin.

Um Hilfe wäre ich dankbar, andernfalls habe ich nämlich auf der Zielgeraden meiner Abschlussarbeit ein Problem 🙂

Gruss PHANTOMIAS
 
Vielleicht denke ich auch falsch.

Mit
Java:
Iterator<Group> iterator = groups.iterator();
    while (iterator.hasNext()) {
will ich eigentlich nur erreichen, dass wenn ein User mit der n:m-Relation zu einer Gruppe sich der Status ändert, sprich ein User is bspw. nicht mehr in der Gruppe vorhanden, dass dann die Zwischentabelle dieses "übernimmt" und den Eintrag herauslöscht.

Gruss PHANTOMIAS
 
ies scheint ein häufiges Problem zu sein, leider konnte ich bisher keine Lösung für mich finden.
Das Problem ist dass die Leute die Doku nicht lesen, und das kommt häufig vor 😉

"Lazy Load" ist dir klar?
Ne Fetch Query würde ich dann als Abhilfe empfehlen.
 
Guter Konter am Sonntag Mittag vorm Deutschland-Spiel 😉

Eigentlich dachte ich, dass ich alle Forenbeiträge, die dazu exitsieren, schon gelesen habe.

Lazy Load ist mir eigentlich klar, es wird erst dann geholt, wenn es benötigt wird.
Vielleicht werfe ich jetzt ganz was durcheinander, aber ist für meinen Fall vllt. Cascading interessant?

Eigentlich will ich ja "nur", dass wenn sich am User bzgl. der n:m-Relation zur Group etwas ändert, dass sich die Zwischentabelle "aktualisiert".

Was meinst du mit einem Fetch Query? Eine Abfrage starten, und die Gruppen zu einem User "händig" holen?

Danke + Gruß PHANTOMIAS.
 
Guter Konter am Sonntag Mittag vorm Deutschland-Spiel
Ach, wollte nur klarstellen dass das kein Hibernate Problem/Bug ist 🙂

Lazy Load ist mir eigentlich klar, es wird erst dann geholt, wenn es benötigt wird.
Richtig, aber dazu muss die Session noch offen sein, diese ist aber offensichtlich schon geschlossen.

Eigentlich will ich ja "nur", dass wenn sich am User bzgl. der n:m-Relation zur Group etwas ändert, dass sich die Zwischentabelle "aktualisiert".
Schon klar, aber die groups Collection ist eben nur Lazy geladen (siehe Fehlermeldung), die Session schon geschlossen, und dann verursacht der Versuch des nachladens die Lazy Init Exception.

Was meinst du mit einem Fetch Query? Eine Abfrage starten, und die Gruppen zu einem User "händig" holen?
So in der Art.
Es gibt sehr gute Gründe warum Collections & User Types per Default Lazy Load sind, nicht Eager, speziell bei M:N Beziehungen (ist bei anderen ORM NEBEN Hibernate auch so).
Mit der Fetch Query kannst du zur Laufzeit eben dafür sorgen dass diese Attribute, passend zum Use Case, doch geladen werden, vom standard Eager Load kann ich bei sowas nur abraten.
 
Es gibt sehr gute Gründe warum Collections & User Types per Default Lazy Load sind, nicht Eager, speziell bei M:N Beziehungen (ist bei anderen ORM NEBEN Hibernate auch so).
Mit der Fetch Query kannst du zur Laufzeit eben dafür sorgen dass diese Attribute, passend zum Use Case, doch geladen werden, vom standard Eager Load kann ich bei sowas nur abraten.

Hm, okay, und wie soll das in meiner UserServiceImpl. dann aussehen?
Zur Zeit habe ich es ja so
Code:
public void updateUser(User u) {
    List<Group> groups = u.getGroups();
 
    // THIS LINE ERROR TRACE
    Iterator<Group> iterator = groups.iterator();
    while (iterator.hasNext()) {
        Group group = iterator.next();
        // ...
    }
    // ...
}
Das kann ich also nicht machen, da u.getGroups() wohl dann null liefert in meinem Fall. Aber wieso lädt das Hibernate nicht automatisch nach? Soll ich nun in meine DaoImplementierung eine Methode schreiben, die dann eine neue Abfrage startet?
Ist das der goldene Weg?

Gruss PHANTOMIAS
 

Neue Themen


Zurück
Oben