# LazyInitializationException vs DTOs



## byte (25. Mrz 2008)

Hi,

ich teste grade Beanlib, um aus Hibernate-Objekten DTOs zu erzeugen. Ich möchte damit die LazyInitExceptions umgehen, wenn ich die Objekte vom Server zum Client übergebe. Wenn ich aber mittels *Hibernate3DtoCopier#hibernate2dtoFully(T entity)* ein DTO erzeuge, werden in den erzeugten Objekten trotzdem PersistentBags als Listen verwendet. Und die schmeissen dann auch die LazyInitExceptions. 

Weiss jemand, wie es richtig funktioniert?

TIA byto


----------



## maki (25. Mrz 2008)

Persönlich wäre ich dagegen DTO einzusetzen wenn man ein Domain Model verwendet, sondern die Domain Objekte selbst per Fetch Join Query laden, ein "dynamisches Eager loading" sozusagen.
Aber meine persönliche Meinung ist hier nicht das Thema 

Was ich mir vorstellen könnte, wäre das diese Bibliothek nicht wirklich den ganzen Objektgraphen traversiert, sondern nur "eine" Ebene, d.h. nicht in die Collections/Attribute reingeht und nie die getter/setter der Proxies aufruft, damit merkt Hibernate nicht dass diese nachgeladen werden sollen und lädt sie auch nicht nach... ist aber nur wild geraten, kenne beanlib nicht.


----------



## byte (25. Mrz 2008)

Doch, es soll ja eben der gesamte Object-Graph kopiert werden:


> Returns a DTO by deep cloning the given Hibernate bean.


----------



## byte (26. Mrz 2008)

maki hat gesagt.:
			
		

> Persönlich wäre ich dagegen DTO einzusetzen wenn man ein Domain Model verwendet, sondern die Domain Objekte selbst per Fetch Join Query laden, ein "dynamisches Eager loading" sozusagen.
> Aber meine persönliche Meinung ist hier nicht das Thema


Jo, so kann mans natürlich auch machen. Aber ich sehe keine gute Lösung, wie ich auf diese Weise bestimmen kann, welche Tiefe des Objektbaumes er laden soll. Man kann das schlecht automatisieren, sondern muss sich das dann jedes mal neu implementieren.
Ausgangspunkt ist, dass ich Domänenobjekte vom Server zu Clients weitereichen möchte. Ich habe also nur die Möglichkeit, DTOs zu erzeugen oder sicherzustellen, dass alle nötigen Daten schon geladen sind. Ich hätte gernen einen flexiblen Mechanismus, der mir zum Beispiel den Objektgraph eines beliebigen Objekts bis zu einer bestimmten Stufe vorlädt oder sogar gezielt bestimmte Properties oder gar Pfade vorlädt.
Daher wollte ich testen, ob Beanlib das hergibt. Oder kennst Du eine andere Lösung? Aber lass mich raten: ihr schreibt Webfrontends und habt die Probleme gar nicht?


----------



## maki (26. Mrz 2008)

Natürlich haben wir diese Probleme, so wie alle, die Hibernate/JPA einsetzen und dann die Objekte "detachen".

DTOs machen die Sache auch komplexer als notwendig, diese Beanlib scheint die Komplexität der DTO Assembler ganz gut zu Kapseln.

Jedenfalls sind DTOs Dinge aus der pre-Domain Model Ära, POJOs machen sie eigentlich überflüssig.

Man muss dazu sagen, dass die klassischen DTO HashMaps, DynaBeans oder ähnliches sind, also nicht die echten Objekte, sondern nur dumme Datencontainer.

Was bleibt ist, das eager/lazy loading zu steuern: Wann brauche ich was?

Die Use-Cases zu analysieren und umzusetzen ist der eigentliche Aufwand, bei welchem Use-Case brauche ich welche Daten?
Dafür dann eigene Methoden im Service bzw. der Facade.

Wir machen das übrigens so:
Was Listen liefert, zB. als ergebnis einer Suche, liefern auch Proxies, Zumindest was von der Liste angezeigt werden soll, wird auf jedenfall geladen.

Wenn man ein Objekt aus der Liste ansehen/editieren will, nutzt man eine Methode der Facade, welche load heisst und die ID als Parameter nimmt. Diese load Methode liefert den kompletten Objektgraphen, über einen fetch join, und daher keine Proxies


----------



## byte (26. Mrz 2008)

maki hat gesagt.:
			
		

> Wir machen das übrigens so:
> Was Listen liefert, zB. als ergebnis einer Suche, liefern auch Proxies, Zumindest was von der Liste angezeigt werden soll, wird auf jedenfall geladen.


Ich überlege derzeit halt, wie man das dynamisch handhaben kann. Angenommen Entität A kennt viele Entitäten B. Nun ist eine View vorstellbar, die alle As zeigt aber keine Bs (B wird nicht geladen). Nun möchte der User sich aber zu einem bestimmten A die Bs anzeigen. Ich muss also alle Bs nachladen. 
Ich frage mich nun: Wie macht man das am geschicktesten? 

Möglichkeit 1: Client ruft alle Bs von A ab und weist sie A zu (so dass A.getBs() keine LazyExc mehr wirft).
Möglichkeit 2: Client ruft A erneut ab, wobei dabei alle Bs mitgeholt werden.
Möglichkeit 3: Eigenen Proxy für den Client schreiben, der in getBs() die LazyExc auffängt und in diesem Fall die Daten vom Server holt. 

Naja, Du hast wahrscheinlich recht und man muss sich das wirklich von Use Case zu Use Case überlegen.


Edit: In meinem Fall ist der Client eine Swing-Anwendung und keine Webapp.


----------



## maki (26. Mrz 2008)

Möglichkeit 3 wäre mir zu aufwändig, und man hätte wieder eine Art Objekt in der View, und eine andere in der Domain, macht die Sache nicht einfacher imho.

Mögl. 1 +2 schon eher, allerdings auch zusätzlicher Aufwand, wobei man immer nur mit Domainobjekten bzw. deren Proxies arbeitet, letzteres ist aber tranparent, ztumindest solange sich der Proxie nicht beschwert 

Wenn du weisst dass es eine View gibt, in der nur As angezeigt, aber keine Bs, sollten Proxies doch reichen.
Wenn der User nun ein A auswählt, kann man dieses dann komplett nachladen, samt allen Bs.

Am Rande des Geländers:
Lazy loading und die dadurch entstehen Probleme dienen nur einem Zweck: Performance, nicht immer alles laden.

Dafür nimmt man in Kauf:
- Das unter Umständen viele einzelne SQL Statements abgesetzt werden, kann sogar langsamer sein als eager loading.
- erhöhte Komplexität, wann brauche ich was?

Am einfachsten, aber nicht empfehlenswert ist es statisch auf eager loading zu setzen, d.h. direkt in den Mappings, dann gibt es auch keine Proxies mehr lol


----------



## byte (27. Mrz 2008)

Mir ist noch eine Möglichkeit eingefallen, wie man das Ganze mit Spring vielleicht generisch und transparent lösen könnte:

Meine Überlegung geht in die Richtung, dass man einen Around-Advice (AOP) schreibt, den man um alle Getter des Domainmodells legt. Nun könnte man beim _proceed()_ die LazyIntitializationException auffangen. In diesem Fall muss man dann irgendwie einen Service anstoßen, der das entsprechende Property nachlädt. Da bin ich mir jedoch noch nicht so sicher, ob man das überhaupt (vernünftig) auf generische Weise hinbekommt. Aber an das Target-Objekt kommt man ja ran. Und das Property kennt man ja über den Getter. Bin (noch) kein Spring-Experte aber sollte in meinen Augen schon realisierbar sein.

Das ganze hätte den Vorteil, dass es komplett transparent wäre. Man kann einfach aller getter auf Client-Seite aufrufen und die Daten werden bei Bedarf nachgeladen.

Meinungen?


----------



## ign0rant (27. Mrz 2008)

FYI: Im aktuellen Java-Magazin 4.08 gibt es einen Artikel zum Thema.



> *Hibernate Preload Pattern*
> 
> Über die berühmt berüchtigte Lazy Loading Exception wurde in der Hibernate-Welt wahrscheinlich bisher am meisten diskutiert. Bekannte Workarounds wie das Open-Session-In-View Pattern sind in vielen Fällen schlicht nicht anwendbar. Dieser Beitrag zeigt ein Verfahren, das Problem durch gezieltes Vorausladen der benötigten Model Instanzen zu lösen: Das Preload Pattern.
> 
> _Jürgen Kohl_


----------



## ms (28. Mrz 2008)

Ich habe den Artikel gelesen, nicht wirklich das gelbe vom Ei.
byto's Idee lazyloading über VM-Grenzen hinweg zu Implementieren ist mir auch schon gekommen und halte ich für einen guten Ansatz.

ms


----------



## maki (28. Mrz 2008)

> Meinungen?


Sehr interessant.

Allerdings müsste man aufpassen, dass man nicht zu oft den Server aufruft, da sonst die Performance im Keller ist und statisches eager loading einfacher & genauso langsam wäre.


----------



## byte (28. Mrz 2008)

Da hast Du sicher recht. Am Ende wirds vielleicht eine Mischung aus beidem sein. Bei kleinen Objektgraphen ist es bestimmt eine Überlegung wert, einfach direkt alles zu laden. Ich habe hier aber einen Anwendungsfall, da gibt es riesige Objektgraphen, über die man per Baum navigieren kann. Da wäre es fatal, auf Lazy Loading gänzlich zu verzichten. Vielleicht bietet sich da einfach an, einen Zwischenweg zu gehen. Damit meine ich, dass man nicht perse alles Eager macht aber auch nicht alles Lazy, sondern einen gewissen Teil vorlädt (z.B. bis Tiefe x im Graph).

Ich habe jetzt mal ein wenig rumgespielt und einen Aspekt geschrieben, der um allen Gettern meines Domainmodels liegt. Da die Modellobjekte von Hibernate kommen und keine Spring-Beans sind, muss man dabei auf Load-Time-Weaving umstellen. Es ist nun ohne weiteres möglich, eine mögliche LazyInitializationException beim Aufruf des Getters abzufangen. In diesem Fall kommt der Aufruf an einen Service auf dem Server, der mit Hibernate Session die Daten nachlädt. Diese Daten müssen dann noch zurückgegeben werden an den Client und im Aspekt gesetzt werden. Das geht an der Stelle natürlich nur sinnvoll per Reflection, da das Target-Objekt ja vom Typ Object ist. Wenn man aber vorraussetzt, dass jedes Property im Modell Getter und Setter hat und man sich auch an die gängige Namenskonvention hält, ist das ja kein Problem.


Update: Funktioniert tatsächlich.


----------



## Medi (15. Apr 2008)

Warum benutzt ihr nicht das Framework Jboss Seam, dort gibt es solche Probleme nicht.


----------



## byte (15. Apr 2008)

Weil Seam ein Webframework ist und man für das Frontend auf JSF beschränkt ist. Außerdem bist Du bei Seam auf den JBoss Application Server beschränkt. Der ist z.B. hier im Konzern nicht freigegeben.


----------



## Guest (15. Apr 2008)

byto hat gesagt.:
			
		

> Weil Seam ein Webframework ist und man für das Frontend auf JSF beschränkt ist.


Achso hier geht es um desktop-anwendungen.



> Außerdem bist Du bei Seam auf den JBoss Application Server beschränkt. Der ist z.B. hier im Konzern nicht freigegeben.



Also ich habe nicht ausprobiert, aber im Buch steht: die Anwendungen sind mit geringen Änderungen auf anderen AS lauffähig(GlassFish, ...).
Man kann in Seam die komplette Geschäftslogik mit Pojos programmieren, dann ist die Anwendung auf J2EE-1.4-Anwendungsservern und auf einfachen Tomcat-Servern lauffähig.


----------



## maki (15. Apr 2008)

Dazu braucht man aber noch kein Seam...

Abgesehen davon geht es hier nicht um Desktop Anwendungen, sondern um Fat/Smart Clients.


----------

