Beim LazyLoading mit Hibernate werden die eigentlichen Entitäten durch Proxy Objekte ersetzt. Diese Proxy Objekte werden erst dann initialisiert, wenn auf sie zugegriffen wird. Hibernate erstellt eine Unterklasse der Entität und überschreibt alle Methoden der Elternklasse. Wird eine dieser Methoden aufgerufen, so delegiert der Proxy die Anfrage an die Datenbank und füllt die Entität aus der Datenbank. Diese Objekte lassen sich mit instanceof auf ihren Typ prüfen und sie können in ihren Ursprungstyp gecastet werden, da es sich ja bei dem Proxy um eine Kindklasse der eigentlichen Entität handelt.
Wenn jetzt aber Objekthierarchien und Polymorphismus mit ins Spiel kommen, dann wird es schwieriger.
Ein Beispiel:
public class Vehicle{ ... } public class Car extends Vehicle{ public void reFuel(){}; } public class Bicycle extends Vehicle{ ... } public class Garage { Set<Vehicle> vehicle = new HashSet<Vehicle>(); public Set<Vehicle> getVehicle(){ return vehicle; } } |
Alle Autos sollen nun aufgetankt werden.
Eine einfache Möglichkeit wäre:
for(Vehicle v : garage.getVehicle()){ if(v instanceof Car){ Car car = (Car)v; car.reFuel(); } } |
Wenn jetzt aber Hibernate mit LazyLoading eingesetzt wird, so werden wir leider bei nächster Gelegenheit mit dem Auto ohne Benzin liegen bleiben. Hibernate ersetzt die einzelnen „Vehicle“ durch Proxies, also durch Kindklassen der Klasse “Vehicle”. Da der Proxy dann aber kein „Car“ ist, schlägt das instanceof fehl und das Auto wird nicht aufgetankt. Selbst wenn wir uns sicher sind, dass nur Autos in der Garage stehen (also auf das instanceof verzichten), schlägt spätestens der Cast nach „Car“ mit einer ClassCastException fehl.
Um sowohl auf das „instanceof“ wie auch auf den Cast verzeichten zu können, können wir das Visitor Pattern einsetzen.
public interface VehicleVisitor{ void visit(Car car); void visit(Bicycle bicycle); } |
Für die Bequemlichkeit einen Adapter:
public VehilceVisitorAdapter implements VehicleVisitor{ void visit(Car car){ /* leer */}; void visit(Bicycle bicycle){/* leer */}; } |
Unsere Fahrzeuge müssen den Visitor entgegennehmen:
public class Vehicle{ public void accept(VehilceVisitor visitor){ // leer } ... } public class Car extends Vehicle{ public void reFuel(){}; public void accept(VehilceVisitor visitor){ visitor.visit(this); } } |
Jetzt kann die ursprüngliche Schleife angepasst werden:
VehicleVisitor fuelVisitor = new VehicleVisitorAdapter(){ public void visit(Car car){ car.reFuel(); } }; for(Vehicle v : garage.getVehicle()){ v.accept(fuelVisitor); } |
Jetzt sind dann auch wirklich alle Autos aufgetankt und wir brauchen keine Angst mehr vor Hibernate Proxies haben. Nebenbei haben wir auf das „hässliche“ instanceof verzichtet und brauchen auch nicht mehr die Objekte zu casten.