Autorenarchiv

Hibernate Proxies und Polymorphismus

Freitag, 07. Mai 2010

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.


Felix Breske


Eclipse Shortcuts Teil 2

Freitag, 08. Januar 2010

Eclipse verfügt über unzählige Shortcuts für nahezu alle Funktionen und Befehle. Jedes mal wenn ich auf der Suche nach neuen Shortcuts war, stieß ich auf lange unüberschaubare Listen. Es waren einfach zu viele neue Befehle um sich etwas zu merken. Neue Befehle habe ich fast nur durch abgucken gelernt. Auf diese Weise lerne ich nur einen neuen Shortcut, der mich dann auch gerade interessiert. Was bringen mir 100 Shortcuts, wenn ich vorher immer überlegen bzw. 2 mal probieren muss um den richtigen zu treffen. Da bleibe ich dann lieber “Mausschubser”.

Für diese Liste habe ich nun den Vormittag über jeden Shortcut, den ich benutzt habe in eine Datei getippt. Für diese Befehle brauche ich nicht nachdenken. Für alles andere verwende ich dann lieber die Maus.

STRG + d – Entfernen einer ganzen Zeile.

ALT + UP/DOWN – Verschieben der aktuellen Zeile nach oben oder unten.

ALT + LEFT/RIGHT – Springen zwischen den letzten bearbeiteten Zeilen.

STRG + 1 -Zeigt für die aktuelle Zeile die möglichen “quick fix” optionen an (z.b. Variable zuweisen, umbennen usw.). Für die meisten Optionen gibt es auch direkte Shortcuts aber die brauche ich mir so nicht merken. Nur durch diesen Shortcut ist diese Liste so kurz geblieben.

STRG + (SHIFT) + j – Suche im aktuellen Editor ohne Dialog. Diese Suche funktioniert ähnlich der Suche im Firefox. Nach dem drücken von STRG + J einfach das gesuchte Wort zu tippen. Im Editor wird dann nächsten möglichen Treffer gesprungen. Mit Shift wird die Suchrichtung geändert. Wiederholtes drücken spring zum nächsten möglichen Treffer.

STRG + SHIFT + t – Direktes öffnen einer Klasse. Neben ganzen Klassennamen können im Suchfeld auch nur die Großbuchstaben einer Klasse eingeben werden, wenn diese einen “Camel Case” Namen trägt (z.B. MTK für “MeineTestKlasse”, wenn es mehrere Treffer gibt dann funktioniert auch “MTKlas”).

STRG + SHIFT + r – Ähnlich der Suche einer Klasse, nur für beliebige Dateien.

STRG + SHIFT + g – “Java Search” für den markierten Code. Sehr hilfreich um Stellen zu finden, an denen eine Klasse verwendet oder eine Methode aufgerufen wird.

STRG + SPACE – Der Klassiker, kennt wohl wirklich jeder. Vervollständigung einer Methode, Klasse oder Variablen.

STRG + o – Zeigt innerhalb einer geöffneten Klasse alle darin befindlichen Methoden an, um dorthin springen zu können. Durch wiederholtes drücken können zusätzlich die geerbten Methoden angezeigt werden.

STRG + t – Zeigt auf einer Klasse die Vererbungshirarchie an, bzw. auf einer Methode die ggf. überschriebenen Methoden der Oberklassen an.

STRG + SHIFT + F – Formatieren der geöffneten Datei. Ist dank der Save Action nicht mehr wirklich notwendig. Ich brauche es nur noch für XML Dateien, da bei mir die Save Actions dort nicht ausgeführt werden.

STRG + 7 – Text aus/einkommentieren.

ALT + SHIFT + R – Umbennen einer Klasse Variablen oder Methode.

Speichern, also STRG + s braucht man ja eigentlich nicht mehr erwähnen. Bei vielen Entwicklern die ich kenne ist STRG + s ein Reflex und kein bewußter Befehl. Copy & Past benutze ich natürlich fast nie beim Entiwckeln ;-) .


Felix Breske


OutOfMemoryError? …. da geht noch was!

Freitag, 28. August 2009

Vor einigen Tagen kam hier im Büro die Frage auf, inwiefern eine Javaprogramm nach einem OutOfMemoryError noch funktioniert.

Das erste kleine Testprogramm war schnell geschrieben. Allerdings zeigte sich dabei, dass es gar nicht so leicht ist den Speicher “fast” voll zu machen um zu sehen was noch funktioniert. Alle StringBuffer, Listen, Vektoren usw. waren nicht geeignet. Diese Klassen arbeiten intern mit Arrays. Sobald ein Array voll ist, wird es durch ein neues mit der doppelten Größe ersetzt. Dabei wird kurzzeitig der doppelte Speicherplatz benötigt. Es ist also wahrscheinlich das der OutOfMemoryError bei genau diesem Vergrößerungsvorgang erzeugt wird. Danach wird das neue doppelt so große Array aber vom GarbageCollector(GC) weggeräumt. Dadurch wird Speicher freigegeben und es kann erstmal normal weitergearbeitet werden (es sei denn man versucht in die Liste nochmal etwas einzufügen, also eine erneute Verdoppelung).

Um dieses Problem zu umgehen, habe ich eine verkette Liste verwendet. Hier kann ich auf alle Elemente eine Referenz halten (damit der GC nicht aufräumt) und brauche kein Array und keine Art von Listenimplementation.

In dem Testprogramm selbst wird nun in zwei Ebenen den Fehler abgefangen. In der ersten Ebene wird noch eine Referenz auf das erste Element in der Liste gehalten. Der GC hat also keine Möglichkeit aufzuräumen bzw. Speicher freizugeben. Je nachdem wann der GC zuletzt gelaufen ist, können noch einige Zeilen in dem catch Block ausgeführt werden. Teilweise reicht schon die Ausgabe des StackTrace um einen erneuten OutOf MemoryError zu erzeugen.

Wird nach dem ersten OutOfMemoryError ein weitere Error erzeugt (in dem catch Block), so wird dieser von der zweiten Ebene abgefangen. Hier wird dann keine Referenz auf das erste Element mehr gehalten. So kann der GC den ganzen Speicher freiräumen und das Programm kann ein zweites mal ohne Einschränkungen gestartet werden. Die Anzahl der erzeugten Objekte in der Liste nimmt dabei nur vom ersten zum zweiten Durchlauf ab. Danach bleibt die Zahl konstant. Es wird also wirklich der komplette Speicher geleert.

import java.io.IOException;
 
public class Test {
    public static void main(final String[] args) throws IOException {
        while (true) {
            try {
                final ListItem firstElement = new ListItem();
                ListItem lastElement = firstElement;
                final Runtime r = Runtime.getRuntime();
                int i = 0;
                try {
                    while (true) {
                        while (true) {
                            System.out.println(countElemtns(firstElement));
                            System.out.println(i++ + " Used Memory: " + (r.totalMemory() - r.freeMemory()) / (1024 * 1024));
                            lastElement = addItem(lastElement, i);
                        }
                    }
                } catch (final Throwable t) {
                    // An dieser Stelle reicht 1 Zeichen mehr im buffer um einen OutOfMemoryError zu erzeugen
                    // final String test = new String("Gefangen"); // das geht trotzdem
                    // System.out.println(test); // das auch
                    t.printStackTrace(); // manchmal reicht das schon zum kaputt machen
                    lastElement = addItem(lastElement, i++);// Unterschiedlich wie oft wir das brauchen um echt einen weiteren OutOfMemory zu erzeugen
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    lastElement = addItem(lastElement, i++);
                    System.out.println(countElemtns(firstElement));
                    System.out.println("Wird meist nicht erreicht");
                    System.out.print("Neustart mit Enter: ");
                    System.in.read();
                }
            } catch (final Throwable e) {
                e.printStackTrace();
                System.out.print("Neustart mit Enter: "); // Keine Referenz mehr auf 'buffer' daher neustart mit leerem speicher moeglich
                System.in.read();
            }
        }
    }
 
    static ListItem addItem(ListItem item, final int i) {
        final ListItem newItem = new ListItem();
        newItem.s = "TEST" + i;
        item.child = newItem;
        item = newItem;
        return item;
 
    }
 
    static int countElemtns(ListItem item) {
        int i = 0;
        while (true) {
            if (item != null) {
                i++;
                item = item.child;
            } else {
                break;
            }
        }
        return i;
    }
 
    static class ListItem {
        ListItem child;
        String s;
    }
 
}

Um die Laufzeit nicht unnötig lang zu machen sollte der Speicher beim starten des Programms möglichst klein sein:

java -Xmx2m -Xms2m Test

Es ist also tatsächlich möglich nach einem OutOfMemoryError weiterzuarbeiten. Hierfür muss nur sichergestellt werden, dass nach dem entstehen des Errors sofort Speicher freigegeben wir. Jede weitere Zeile Code kann sonst zum nächsten Fehler führen.

Interessant wäre noch zu wissen ob es möglich ist den Speicher soweit zu füllen, dass bereits der Aufruf des GC zu einem weiteren Error führt. Viel Speicher kann er nicht benötigen wenn bereits das printStackTrace zum nächsten Fehler führt. Ggf braucht er gar keinen zusätzlichen Speicher bei der Ausführung, sondern hat schon alle im Speicher nach dem ersten Start?

In dem Programm habe ich wohl gegen alle Regeln des sauberen Programmierens verstoßen. Als Programmieren sollte man nie in die Situation kommen mit Throwable bzw. Error Klassen zu arbeiten. Im Fall eines OutOfMemoryErrors kann man wie oben beschrieben tatsächlich weiterarbeiten wenn genug Speicher freigegeben wurde, aber z.B. ein Error durch einen Festplattendefekt wird wohl kaum durch den GC behoben (oder kann der GC Festplatten reparieren? ).

Nach der Entwicklung eines solchen Codes bitte das Händewaschen nicht vergessen.


Felix Breske


Transaktionssteuerung mit Spring AOP

Freitag, 24. April 2009

Spring bietet uns mit AOP die Möglichkeit innerhalb der Spring Konfiguration die Behandlung der Datenbanktransaktionen zu steuern. Hierfür benötigen wir lediglich eine AOP Konfiguration, die auf alle Methoden zeigt, die innerhalb einer Transaktion ablaufen sollen und den Spring Tag .

In diesem Beispiel gehen wir von zwei Service Klassen aus, deren Methoden von einer Transaktion umschlossen werden sollen.

applicationContext.xml:

<-- service beans -->
<bean id="fooService" class="a.b.service.FooService" />
<bean id="barService" class="a.b.service.BarService" />
 
<-- hibernate transaction manager -->
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
 
<-- hibernate session factory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource">
        <ref bean="dataSource" />
    </property>
    <property name="annotatedClasses">
        <list>....</list>
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
        </props>
    </property>
</bean>

Innerhalb des tx:advice definieren wir das Verhalten für die einzelnen Methoden der Services. Für alle Methodenaufrufe werden die gleichen Isolation- und Propagation-Level benutzt. Für Methoden die mit „find“ oder „get“ beginnen setzen wir die Transaktion noch auf read-only. In diesem Methoden dürfen jetzt nur noch Datenbank SELECTs ausgeführt werden, anderenfalls würde es eine Exception geben. Die Datenbank kann so das Locking auf Zeilen, die von diesem Methodenaufrufen betroffen, sind optimieren.

applicationContext.xml:

<tx:advice id="defaultTranscationAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="*" isolation="READ_COMMITTED" propagation="REQUIRED" />
        <tx:method name="find*" read-only="true" />
        <tx:method name="get*" read-only="true" />
    </tx:attributes>
</tx:advice>

Als letztes brauchen wir jetzt noch die AOP Konfiguration, die das defaultTransactionAdvice mit den Service Klassen verbindet. Durch die Definition „a.b.service.*.*(..)“ wird um jede Methode in jeder Klasse innerhalb des Paketes a.b.service eine Transaktion gelegt. Es können so weitere Service Klassen hinzugefügt werden, ohne dass das Transaktion Handling angepasst werden muss.

applicationContext.xml:

<aop:config>
    <aop:pointcut id="allServiceMethods" expression="execution(* a.b.service.*.*(..))" />
    <aop:advisor advice-ref="defaultTranscationAdvice" pointcut-ref="allServiceMethods" />
</aop:config>

Innerhalb der Service Methoden brauchen wir uns nun keine Gedanken mehr um offene oder geschlossene Transaktionen machen. Wir bewegen uns in jedem Fall innerhalb einer Transaktion. Sollte es zu einem Fehler kommen, so wird ein Rollback auf allen Änderungen der Methode durchgeführt.
Erst beim Verlassen der Service Methode müssen wir berücksichtigen, dass Objekte ggf. nicht mehr an eine Session gebunden sind (hier kann es dann zu Problemen mit LazyLoading o.ä. kommen).


Felix Breske