Preload Pattern aus Java Magazin 4/08 fehlerhaft

Status
Nicht offen für weitere Antworten.

byte

Top Contributor
Hallo,

habe mich nun einmal mit dem Preload-Pattern für Hibernate aus dem Java Magazin (Ausgabe: April 2008) beschäftigt und festgestellt, dass der Code fehlerhaft ist.

Wenn man dem GenericDao beispielsweise eine Methode findAll(Preload[] preloads) spendiert, dann wird gar nichts vorgeladen. Schuld daran ist folgende Zeile:

Code:
if(Hibernate.isInitialized(entity)) {
   return;
}

Die übergebene Entität ist in diesem Fall ja eine Collection und die ist schon von Hibernate initialisiert, d.h. die Preload-Methode wird an dieser Stelle beendet ohne die preloads vorzuladen. Kommentiert man den Code-Block aus, dann funktioniert es. Allerdings wird es dann wohl zu ner Endlosrekursion kommen bei bidirektionalen Verbindungen - das habe ich allerdings noch nicht getestet.

Hat das jemand schonmal ausprobiert und eine hübsche Lösung für das Problem? Eine Möglichkeit wäre, dass man sich die Referenzen der Objekte merkt, welche schon abgearbeitet wurden, um somit eine Endlosrekursion zu verhindern. Finde ich aber eher unschön, zumal es auch Speicher kostet.

Hat jemand eine bessere Idee?

Gruß byto
 

byte

Top Contributor
Man merkt schnellt, wenn man ein bißchen im Internet liest, dass ein Großteil der Hibernate-User Webanwendungen schreiben. Häufig sind die auch einfacherer Natur, so dass man mit dem Open Session in View Pattern gut zurecht kommt. Da hat man diese Probleme ja eh nicht.

Und wo OSiV nicht zieht, da benutzen scheinbar viele immernoch DTOs. Zumindest liest man das noch sehr oft in verschiedenen Foren.

Und wenn man zu den coolen gehört und auch mit detachten Hibernate Objekten arbeiten will/ kann ( ;) ), dann muss man sich halt erstmal überlegen, wie man damit umgeht. Ich schätze mal, dass viele häufig einfach spezielle Dao-Methoden implementiert, die genau das fetchen, was man braucht. Das ist natürlich mehr Arbeit als einfach dieses Preload-Pattern anzuwenden.
Aber dieses Pattern ist ja auch nicht der Weisheit letzter Schluss. Das Problem ist nämlich, dass pro Preload ein Select abgesetzt wird. Die Performance ist also besser, wenn Du direkt alle Daten per "Fetch Join" holst. Wirklich nett wäre es, wenns dafür ein generisches Pattern gäbe. ;)
 

ms

Top Contributor
Ich finde das Preloadpattern ist fachlich gesehen inakzeptabel, da Schnittstellen um einen technischen Aspekt angepasst bzw. erweitert werden müssen. Und bei überladenen Methoden mit mehreren Parametern wirds wirklich hässlich.

Mir würde viel lieber ein echtes Remote-Lazyloading gefallen wobei man zusätzlich über Annotations steuern kann was genau preloaded werden soll.

Code:
public class PersonDelegate implements PersonService {

	private PersonService personService = null;
	
	@Override
	@Preload (preloadPath = {"addresses"})
	public List<Person> findById() {
		return personService.findById();
	}

}
Die Klasse Person hält eine Liste von Adressen.

Naja, Weihnachten kommt ja bald.


ms
 

byte

Top Contributor
ms hat gesagt.:
Ich finde das Preloadpattern ist fachlich gesehen inakzeptabel, da Schnittstellen um einen technischen Aspekt angepasst bzw. erweitert werden müssen. Und bei überladenen Methoden mit mehreren Parametern wirds wirklich hässlich.

Mir würde viel lieber ein echtes Remote-Lazyloading gefallen wobei man zusätzlich über Annotations steuern kann was genau preloaded werden soll.

Code:
public class PersonDelegate implements PersonService {

	private PersonService personService = null;
	
	@Override
	@Preload (preloadPath = {"addresses"})
	public List<Person> findById() {
		return personService.findById();
	}

}
Die Klasse Person hält eine Liste von Adressen.

Naja, Weihnachten kommt ja bald.


ms

Das könntest Du ja recht einfach realisieren, wenn Du im Hintergrund das Preload-Pattern implementiert hast, indem Du z.B. per Reflection die Preload-Annotation einliest und dementsprechend die Preloads gemäß des Patterns erzeugst.
Mir persönlich ist der Ansatz aber viel zu oversized. Unter Umständen hast Du dann x-mal den gleichen Service. Ich sehe kein Problem darin, dass der Client entscheidet, welche Daten er benötigt. Der Service kann und soll das gar nicht wissen.
Grundsätzlich gebe ich Dir aber recht: technische Aspekte in der Fassade sind nicht so toll. Aber bei Deinem Ansatz wäre dieser Aspekt ja nur an eine andere Stelle verlagert. Der Client müsste ja auch dort irgendwo entscheiden, welches Service-Delegate er verwendet. Das ist ja auch eine technische Entscheidung, genauso wie das übergeben der Preloads bei dem Pattern. Doch hier ergibt sich zusätzlich noch die Schwierigkeit, wie der Client überhaupt entscheiden kann, welches Service-Delegate er braucht. Das müsste dann über Schnittstellen-Dokumention geregelt sein. Nicht so prickelnd. Und der Entwicklungsprozess wäre auch recht mühsam, wenn man bedenkt, dass häufig ein Team den Client und ein anderes den Server entwickelt. Wenn Team Client neue Daten braucht, müssen sie erst auf Team Server warten, bis das entsprechende Delegate verfügbar ist.

Mir gefällt im Moment eine Mischung aus dem Preload-Pattern und meinem Ansatz des On Demand Preloadings am besten. ;)
 
S

SlaterB

Gast
der Client fragt beim Server Objekt A oder Objekt B an,
wenn er darüber entscheidet, dann kann er genausogut die Information mitgeben, ob er Attribut C und/ oder D vom Objekt A braucht und der Server preloaded das dann

loadA()
vs
loadAWithC()
 

ms

Top Contributor
byto hat gesagt.:
Mir persönlich ist der Ansatz aber viel zu oversized. Unter Umständen hast Du dann x-mal den gleichen Service. Ich sehe kein Problem darin, dass der Client entscheidet, welche Daten er benötigt. Der Service kann und soll das gar nicht wissen.

Natürlich ist das mit einem Overhead verbunden, die Frage ist nur wie sich dieser Overhead niederschlägt und ob er akzeptabel ist. Dass es sich auf die Performance schlägt denke ich nicht, im Gegenteil, Lazyloading mit Preloading ist ja letztendlich eine Optimierungsmaßnahme.
Und für den Cliententwickler wäre es eine sehr übersichtliche Variante.

Vorteil wäre, dass jeder Client - und gehen wir mal davon aus, dass es mehrere Clientapplikationen gibt die unterschiedliche Preloads möchten - seine Preloads selbst definieren kann, unabhängig wie der Server implementiert ist.

Grundlage dafür müsste wie schon erwähnt ein "echtes Remotelazyloading" sein. Damit meine ich dass auf der Clientseite eine Art Session geöffnet werden muss, dann die Servicemethode ausgeführt wird und man bekommt ein Proxyobjekt zurück, welches selbständig beim Zugriff auf einen Getter nachlädt. Über diese Session wird das nachladen und das Preloading gesteuert.

byto hat gesagt.:
Grundsätzlich gebe ich Dir aber recht: technische Aspekte in der Fassade sind nicht so toll. Aber bei Deinem Ansatz wäre dieser Aspekt ja nur an eine andere Stelle verlagert. Der Client müsste ja auch dort irgendwo entscheiden, welches Service-Delegate er verwendet. Das ist ja auch eine technische Entscheidung, genauso wie das übergeben der Preloads bei dem Pattern. Doch hier ergibt sich zusätzlich noch die Schwierigkeit, wie der Client überhaupt entscheiden kann, welches Service-Delegate er braucht. Das müsste dann über Schnittstellen-Dokumention geregelt sein.
Nicht der Server sondern der Client implementiert den Delegate und definiert damit, was er preloaded haben möchte
Wie bei Hibernate selbst stelle ich mir diese Schnittstelle aus fachlicher Sicht komplett transparent vor.

Ich bin mir auch nicht sicher ob meine Gedankenspielerei überhaupt realisierbar ist.

ms
 

byte

Top Contributor
ms hat gesagt.:
Dass es sich auf die Performance schlägt denke ich nicht, im Gegenteil, Lazyloading mit Preloading ist ja letztendlich eine Optimierungsmaßnahme.
Nicht zwangsläufig, siehe n+1 Selects Problem. Bei jedem Lazy Loading geht ein Select an die Datenbank. Wenn Du vorher weisst, welche Daten Du brauchst, dann ist es besser diese direkt per Join zu holen. Dann hast Du 1 Select statt n+1.

Und für den Cliententwickler wäre es eine sehr übersichtliche Variante.
Ich finde es übersichtlicher, die Preloads mitzugeben, weil dadurch nicht viele unnötige Objekte (Delegates) entstehen.

Vorteil wäre, dass jeder Client - und gehen wir mal davon aus, dass es mehrere Clientapplikationen gibt die unterschiedliche Preloads möchten - seine Preloads selbst definieren kann, unabhängig wie der Server implementiert ist.
Das kann er beim Preload-Pattern doch auch. Er gibt einfach mit an den Service, welche Daten dieser Laden soll.
In Deinem Lösungsansatz hat 1 Client auf einmal n-mal den gleichen Service, wenn er n-mal eine andere Sicht auf die gleichen Entitäten benötigt. Es ist in einem Fat-Client nicht unüblich, dass Du verschiedene Views auf die gleichen Daten hast.

Grundlage dafür müsste wie schon erwähnt ein "echtes Remotelazyloading" sein. Damit meine ich dass auf der Clientseite eine Art Session geöffnet werden muss, dann die Servicemethode ausgeführt wird und man bekommt ein Proxyobjekt zurück, welches selbständig beim Zugriff auf einen Getter nachlädt. Über diese Session wird das nachladen und das Preloading gesteuert.
Schau Dir bitte den von mir verlinkten Thread an. Ich habe voll transparentes Remote Lazy Loading mit Spring implementiert.


Nicht der Server sondern der Client implementiert den Delegate und definiert damit, was er preloaded haben möchte
Wie bei Hibernate selbst stelle ich mir diese Schnittstelle aus fachlicher Sicht komplett transparent vor.
Du kannst es doch gar nicht komplett transparent machen, weil 1 Client auch gerne mal mehrere Sichten auf die gleichen Entitäten braucht (s.o.). Er muss also zwangsläufig irgendwo entscheiden, welche Daten er haben will.
 

byte

Top Contributor
So richtig schön finde ich die Lösung aber trotzdem nicht. Mein Kritikpunkt ist der, dass pro angegebenen Preload ein Select gemacht wird (N+1 Selects Problem).
Ich habe mir mal testweise einen Mechanismus geschrieben - ich nenne ihn Prefetch Pattern ;) - der ähnlich funktioniert, die Properties jedoch per JOIN lädt (nur 1 SELECT). Ich versuche das ganze noch etwas flexibler zu machen, so dass man individuell entscheiden kann, was per join und was per select geladen wird. Erste Tests sehen aber vielversprechend aus.
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen


Oben