JPA/Hibernate: Objekt vollständig laden, wenn die Session geschlossen wird?

DarthShader

Erfahrenes Mitglied
Hallo zusammen,

ich möchte ein Objekt via Hibernate aus der Datenbank laden und es nach Schließen der Session verwenden. Ich weiß um den Mechanismus mit der Instrumentation und dem Lazy Loading, sodass das Objekt nach und nach die gewünschten Properties bei Zugriff nachlädt.

Nun dachte ich jedoch, dass das Flushen und/oder Schließen der Session bewirkt, dass das Objekt vollständig geladen wird, dies scheint aber nicht der Fall zu sein.

Hier ein kleines Beispiel:

Java:
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();

Plane plane = new Plane();
plane.setName( "Ein Flugzeug" );
session.save( plane );

transaction.commit(); // (1)
session.close(); // (2)

session = sessionFactory.openSession(); // (3)
transaction = session.beginTransaction(); // (4)

Plane loadedPlane = (Plane)session.load( Plane.class, plane.getId() );

session.clear(); // (5)
session.flush(); // (6)

transaction.commit();
session.close();
sessionFactory.close();

System.out.println();

Führe ich das aus, wirft er eine

Java:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session

Exception - was ja auch verständlich ist, denn ich greife ja auf das Objekt, welches noch nicht vollständig geladen ist, zu, obwohl die Session geschlossen ist. Allerdings dachte ich, dass wegen (5) und/oder (6) das Objekt vollständig geladen wird. Denn was macht es für einen Sinn, noch nicht vollständig geladene Objekte (attached) zu haben, jedoch eine geschlossene Session?

Ich kenne nur einen "Workaround", um das zu umgehen (bzw. das Objekt zu "detachen"): man cloned es, z.B. mit "Dozer".

  1. Etwas, was ich nicht begreife ist, warum mit (1) bis (4) eine Exception geworfen wird. Nehme ich diese Zeilen nämlich raus, so wird keine Exception geworfen - kann mir das jemand erklären?
  2. Gibt es einen anderen Weg, um das Objekt vollständig zu laden, wenn die Session geschlossen wird (ja, ich möchte Lazy Loading ansonsten aktiviert lassen)?


Über Eure Hilfe würde ich mich sehr freuen


Vielen Dank!
 
Hi,

füg doch in der Zeile unter dem laden ein
Java:
Plane loadedPlane = (Plane)session.load( Plane.class, plane.getId() );
loadedPlane.getTheLazyAttribute(); // lade das Attribut das lazy ist

Wie soll das sonst funktionieren? Deine Idee das bei einem Flush automatisch alle Attribute mit Lazy nachgeladen werden ist rechter Wahnsinn. Warum?

Denk mal nach, warum Lazy sinn macht, genau, weil Du unter Umständen anderenfalls die gesamte Datenbank in den Speicher lädst. Mit lazy schiebst Du einen Riegel davor.

Würdest Du jetzt beim flush extra ALLE Attribute laden, die mit lazy markiert sind, würdest Du auch ALLE Attribute verbundener Entities laden und so sehr wahrscheinlich die komplette Datenbank in den Speicher laden.

Finde schon gut, dass dein Beispiel so funkioniert, wie es das macht ...
 
Hallo Sentoo,

Hi,

füg doch in der Zeile unter dem laden ein
Java:
Plane loadedPlane = (Plane)session.load( Plane.class, plane.getId() );
loadedPlane.getTheLazyAttribute(); // lade das Attribut das lazy ist

Da hinkt natürlich das Beispiel, denn in meinem echten Domain Modell müsste man da "etwas mehr" manuell aufrufen. Aber das ist auch gar nicht so recht das Thema, mir ist natürlich klar, was Lazy Laoding ist, wozu es gut ist, und dass man die Sache umgehen könnte, wenn man explizit alle Properties "anfordert".

Wie soll das sonst funktionieren? Deine Idee das bei einem Flush automatisch alle Attribute mit Lazy nachgeladen werden ist rechter Wahnsinn. Warum?

Ich gehe mal davon aus, dass Du das mit dem Wahnsinn nicht so gemeint hast ;)

Denk mal nach, warum Lazy sinn macht

Wie schon gesagt, mir ist klar, wofür es da ist.

Ich denke, mein Anliegen zielt eher auf das allgemeine Thema des Detaching von Persistent Entities hinaus - und die Idee das mit "flush" oder "close" der Session zu verbinden, kommt daher, da ich mir dachte, man könne während einer offenen Session mit dem Entity normal arbeiten und dabei alle Vorzöge des Lazy Loading genießen. Beim Schließen könnte ich mir dann vorstellen, dass das Objekt automatisch detached wird (wobei ich mit "detachen" meine, dass die Proxys entfernt werden und das Objekt komplett geladen wird).

Denn wenn man die Session schließt, kann man mit einem Objekt zurückbleiben, welches eine Exception werfen kann, nur weil man auf ein Property zugreift. Das kann man leicht übersehen und das Verhalten wird nach außen hin nicht klar dargestellt - man muss es eben wissen.

Dem Design nach scheint es also besser zu sein, beim Schließen der Session mit einem Objekt zurück zu bleiben, das leicht eine Exception werfen kann, als sicher zu stellen, dass dieses "komplett" ist (wie gesagt, man kann die Vorzüge von Lazy Loading ja nutzen, während die Session noch offen ist).

Noch bleibt es für mich eine Design-Entscheidung, ich habe für mich noch nicht das überzeugende pro/contra Argument gefunden.
 
Hallo minjaman,

nein, Gilead kannte ich bisher noch nicht. Klingt aber interessant, vielen Dank für den Link.

Bisher habe ich immer mit Dozer gearbeitet. Die Kombination mit Hibernate und Dozer klappte sehr gut, vor allem um die von Hibernate verwendeten Proxy Klassen los zu werden, wenn man das Persistent Entity z.B. via RMI an ein entferntes System schickt, oder eben auch, um GWT-Kompatible Klassen zu verwenden (wird ja auch auf der Gilead Seite erwähnt wie ich gesehen habe).
 
Mal abgesehen von diversen Lösungsansätzen (z.B. mit AspectJ) zum dynamischen Nachladen von nicht initalisierten Objekten bietet z.B. OpenJPA das definieren sogenannter Fetch-Groups. Mit Hilfe dieser ist es möglich beim Laden einer Entity zu definieren, welche Beziehungen mitgeladen werden sollen und welche nicht.
Ein etwas unschöner Workaround ist außerdem, dass du beim Laden der Entity die gewünschten Beziehungen über den Getter aufrufst und dann zB bei Collections size() oder hashcode() aufrufst, was dann Hibernate dazu veranlasst die Beziehungen mitzuladen.

Gruß

Sebastian
 
Zurück