LazyInitializationException bei Spring / Hibernate

  • Themenstarter Themenstarter Konstantin Denerz
  • Beginndatum Beginndatum
Ich programmiere Transaktionen nicht von Hand, zumindest nicht in den Umgebungen und nicht freiwillig.

Wollen wir es mal etwas umformulieren: Wir haben eine BusinessMethode auf einem SpringBean, die als Wert ein Hibernate oder JPA-Pojo zurückgibt und die wir dann in einem sagen wir mal Servlet aufrufen, d.h. nicht in einer SpringBean. Dieses Pojo hat eine Collection die mit LazyLoad konfiguriert ist und auf die ich mittels getter (nachher zugreife)


Pojo pojo = myBean.myMethod(...);
System.println(pojo.getLazyLoadedCollection() );

Meinetwegen holen wir uns das Bean mit WebApplicationContextUtil oder wie dieses Ding auch immer heißt, oder über den ApplikationContext direkt

Der Aufruf auf des Getter auf diese Collection ist das interessante. Was passiert? Vermutlich LazyLoadingException ?!
 
Ich versteh schon was du vorhast. Das ist ja auch alles richtig, nur fliegt im Testcase keine LLE, wenn du von der richtigen Klasse erbst.

Ich weiß nicht, ob du bewusst nicht verstehen willst, was ich schreibe oder was sonst das Problem ist. Dein Ausgangsproblem schien es zu sein, dass in einer unmanaged Bean (deine m Unittest) die Transaktion - wenn du sie um die Businessmethode gelegt hast - logischerweise zu Ende ist. Dies umgehst du, in dem du mit @Transactional auch die Unittestmethode transaktional machst, was einfach den Transaktionsraum "aufweitet". Und jetzt schreib ich es gern noch zum dritten mal, genau dies tut ATSCT out of the Box für JEDE Testmethode. Schau doch einfach mal in die ApiDoc:

http://static.springframework.org/s.../AbstractTransactionalSpringContextTests.html

Gruß
Ollie
 
Auch wenn ich alt und stur und manchmal unbelehrbar bin: Ich habe es verstanden. Nur war der JUnitTest kein Problem, für das ich eine Lösung gesucht habe, sondern ein Phänomen, dessen Ursachen ich für sehr weitreichend halte:

Bei LazyLoading und meinem so geliebten Persistent DomainModel muss man höllisch aufpassen. Unsere Diskussion hat sogar noch etwas konstruktives, weil sie meinen Verdacht, den ich hatte ja letztendlich bestätigt (leider).

Jetzt beziehe ich mich auch jetzt nicht auf den JUnitTest, sondern auf das was vorher schon zu dem Thema stand: Mögliche Lösungsansätze sind:

1) Das schon erwähnte Filter, was das Problem aber nicht in allen Umgebungen löst und wo ich auch bezweifle, das es für nicht mangedBeans funktioniert. Aber mir fehlt damit die Erfahrung.

2) LazyLoading abhacken

3) Einen Weg finden über Konfigurationsmöglichkeiten der sessionfactory, wo ich mich nicht besonders auskenne. Was allenfalls irgendwelche Timeouts sein könnten, die die Session erst zeitversetzt schließen, was sicher keine wirkliche Lösung zu sein scheint.

4) AspectJ und Loadtime Waeving um den Preis erheblicher zusätzlicher Komplexität

5) nicht von aussen auf die Collections zugreifen, d.h. ein rein serviceorientierter Ansatz (positiv formuliert, ich nenne es: einen rein prozeduralen Ansatz!)

Ich werde in Zukunft sehr viel vorsichtiger sein, und nach Möglichkeit noch stärker Prototyp basiert arbeiten, um nicht in diese Fallen zu laufen Vielleicht ist es auch an der Zeit sich wieder mal etwas neuem zuzuwenden und Java und Spring lebewohl zusagen?
 
Okay... wie gesagt, dein erster Post machte halt den Eindruck, als wäre dein Hauptproblem der Unittest.

Grundsätzlich führt die Problematik zu dem Thema, ob Services überhaupt Domänenobjekte an Clients zurückgeben sollten. Dies hat nämlich zur Folge, dass der Client recht eng an den Server gekoppelt ist. Ausserdem zieht das die hier nun ausführlich diskutierte LL Problematik nach sich.

Lösen könnte man das ganze, in dem der Service DTOs zurückgibt die eine clientspezifische Sicht auf das Domänenmodell repräsentieren. Wenn du dazu noch ein Rich Domain Model verwendest, hätte der Servicelayer auch wieder die Aufgabe, die er eigentlich hat: Demarkation von Transaktionsgrenzen und Aufbereitung der Daten für den Client. Drawback bei der ganzen Geschichte ist dann natürlich, dass die DTOs zu weiten Teilen identisch zu den Domänenklassen sind, nur eben mehr "anemic".

Bisher habe ich folgende Erfahrungen gemacht: Laufen Client und Server in der gleichen VM (klassische Webanwendung), lohnt es meiner Meinung nach nicht, einen Extra DTO Laxer einzuführen. Hier machen OpenSessionInView und ähnliche Paradigmen Sinn.

Bei physisch verteilten Anwendungen (RichClient + Server Backend) macht es Sinn mit DTOs zu arbeiten und im Service die Daten vorzubereiten, auch des Netzwerktraffics wegen.

Den Aufwand, DTOs zu schreiben und zu pflegen umgeht man meist durch modellgetriebene Ansätze in solchen Projekten (aka. Codegenerierung).
 
Hallo,

Grundsätzlich führt die Problematik zu dem Thema, ob Services überhaupt Domänenobjekte an Clients zurückgeben sollten. Dies hat nämlich zur Folge, dass der Client recht eng an den Server gekoppelt ist. Ausserdem zieht das die hier nun ausführlich diskutierte LL Problematik nach sich.
Eine Möglichkeit den Client vom bestimmten Serverseitigen Implementierungen von Domain Objects zu entkoppeln ist
der, entsprechende Interfaces zu definieren und dafür Proxies auf Clientseite zu erzeugen.
Siehe: http://www.tutorials.de/forum/java/250691-dynamisch-zur-laufzeit-getypte-modelle-erzeugen.html
Bei diesen Proxies wird dann zwischen einfachen get/set und "Business Logic" Calls zu unterscheiden.
Die get/set Calls werden auf dem Client direkt behandelt. Aufrufe von "Business Methods" werden dann entweder direkt Remote abgesetzt
oder je nach Konfiguration verzögert, asynchron.

Über diesen Ansatz kann man über entsprechende InvocationHandler / Interceptoren sehr flexible weitere Funktionalität in die Modelle
stecken (e.g. LazyLoading....) ohne das der Anwender was davon mitbekommt. D.h. der Anwendungsentwickler arbeitet nur mit den Interfaces und weis wie er
die Modelle Insatziieren kann und kann ganz einfach mit arbeiten, ohne sich über irgendwelche interna Gedanken machen zu müssen.

...
Drawback bei der ganzen Geschichte ist dann natürlich, dass die DTOs zu weiten Teilen identisch zu den Domänenklassen sind,
nur eben mehr "anemic".
Wenn man diesem Interface-Ansatz folgt hat man im Endeffekt nur eine Implementierung... und ein Interface, anstatt zwei "Implementierungen".

Den Aufwand, DTOs zu schreiben und zu pflegen umgeht man meist durch modellgetriebene Ansätze in solchen Projekten (aka. Codegenerierung).
Bei der Interface-basierten Variante erledigt die IDE, einen Großteil der Arbeit ;-)


Gruß Tom
 
... Ich habe noch etwas gefunden: JPA definiert einen erweiterten PersistenceContext, der das Session in ConversationPattern abbildet. Die Lazy Loadinge Exceptions verschwinden auch in Spring sofort, wenn man den PersistenceContext entsprechend definiert. Aber ich habe nicht in einem WebContainer mit mhreren Sessionsgetestet. Dedacht war das ganze wohl ürsprünglich so, das der Entitymanager innerhalbe eine Stateful Sessionbean definiert wird und an deren Lifecycle hängt. Das könnte bedeuten, das ServiceBean und DAO keine Singeltons mehr sein dürfen (was aber kein Problem wäre, es schnell zu ändern).

Auch im Transaktionalen PersistenceContext sollte es gehen eine BasisKlasse für die FormControler mit (Transaction propagation=Propagation.NOT_SUPPORTED) zu definieren und alle FormController davon abzuleiten. Gleiches gilt für JSF (dann mit Aspect) und vermutlich sollte es auch in Servlets so funktionieren.

Das Transactional über den FormControllern ist sicherlich etwas unschön...

Das hier sind noch keinenfalls praxiserprobte DInge, sondern nur Ideen, daher Vorsicht ! (aber der Reitz liegt hier in der Einfachheit)

Zumindest probiert habe ich, das Transactional(...) auch bei Vererbung funktioniert d.h berüchsichtigt wird.


Der Erweiterte PersistenceContext muss vermutlich bevor man ihn benutzt (in Spring, nicht bei Sessionbeans) noch etwas genauer analysiert werden.
 
Zurück