There is a english version of the article available.

Die meisten Teams setzen Hibernate für Webanwendungen ein. Schon allein, weil wesentlich mehr Webanwendungen erstellt werden, als klassische Client-Server-Systeme. Ich dagegen setze es zur Zeit in einer Swing basierten Zwei-Schichten-Architektur ein. In Webanwendung ist die Interaktion mit der Datenbank und Hibernate stark strukturiert: Der Server erhält eine Anfrage, bearbeitet sie und liefert das Ergebnis in Form einer Webseite wieder zurück. Für genau diesen Zeitraum wird in Webanwendungen eine Hibernate Session verwendet und die Hibernate Session ist so implementiert, dass genau dies gut funktioniert.

  • Die Session repräsentiert eine 'Unit of Work', also eine Arbeitseinheit. Diese kann identisch mit einer Datenbanktransaktion sein, kann sich aber auch über mehrere Datenbanktransaktionen erstrecken.
  • Es ist eine Id-Map Jede Entität gibt es in einer Session nur einmal.
  • Eine Session ist ein First Level Cache für die Datenbank: Jede Entität die in der Session verwendet wird, wird in der Session gehalten, bis die Session beendet wird.
  • Um Lazy Loading zu verwenden muss ein Objekt einer Session zugeordnet sein, und ein Objekt kann (im wesentlichen) nur einer Session zugeordnet sein.


Die 'Unit of Work' ist in Form eines Requests bei Webapplikationen einleuchtend und eindeutig identifizierbar, wie oben beschrieben.

In einer Zwei-Schichten-Architektur sieht die Welt jedoch anders aus. Zunächst einmal ist es verlockend, ähnlich zu arbeiten, wie man es von JDBC Verbindungen gewöhnt ist: Man beginnt eine Session und arbeitet mit dieser Session bis zum Ende der Anwendung. Dies hat offensichtliche Vorteile: Die Id-Map stellt sicher, dass jede Entität eindeutig in der JVM repräsentiert wird. Aktualisierung von GUI-Komponenten funktioniert dadurch praktisch von alleine. Aber es hat ebenso offensichtliche Nachteile. Durch das Caching in der Session, wächst die Session immer weiter an. Früher oder später führt dies unweigerlich zu Speicherproblemen. Es gibt Anwendungen, für die dies akzeptabel ist, viele Anwendungen benötigen aber ein anderes Konzept. Um ein solches Konzept zu entwickeln sollte man sich im klaren sein, was man eigentlich will.

Für uns war dies:

  1. Lazy Loading muss funktionieren, da dies eins der Hauptargumente für, bzw. Vorteile von Hibernate sind, gegenüber einer manuell implementierten Variante auf Basis von JDBC
  2. Verschiedene Ansichten einer Entität müssen synchron gehalten werden.
  3. Das ganze muss muss halbwegs stabil und zuverlässig sein, so dass auch Otto Normalentwickler damit umgehen kann.


Anforderung Nr.1 zwingt dazu, das jedes Objekt, welches aus der Datenbank geladen wird, in einer Session verbleibt. Um zu verhindern, dass wir Objekte auf ewig in einer Session sammeln, muss eine Session zu einem definierten Zustand wieder geschlossen werden, natürlich erst, wenn die enthaltenen Objekte nicht mehr verwendet werden.

Aber wo ist die Session (oder Unit of Work)? Man stellt sich etwas vor wie: Daten laden, Daten ver- bzw. bearbeiten, Daten speichern.

Wenn ich eine Liste von Personen anzeige, starte ich damit also wohl eine Session.

Wenn ich eine Person auswähle, editiere und speichere, beende ich damit ein Session. Problem gelöst? Mit nichten! Ich wähle ein zweite Person zur Bearbeitung aus und schon habe ich eine Session geöffnet, und zwei beendet?

Zu einem gegebenen Zeitpunkt kann es beliebig viele Dialoge oder Fenster in der Anwendung geben. Objekte werden per Drag'n'Drop von einem Fenster ins nächste gezogen. Alles kann zumindest prinzipiell mit allem interagieren. Wo ist da die Session?

Meine Antwort: Im wesentlichen hat jedes Fenster seine eigene Session, Modale Dialog verwenden die Session ihres übergeordneten Fensters, Aktionen die im Hintergrund laufen haben ebenfalls ihre eigene Session. Wird das Fenster geschlossen, oder die Hintergrundaktion abgeschlossen, wird auch die Session geschlossen. Daher ist es wichtig, dass kein Objekt, dass in einem Fenster bearbeitet wird, den Einflussbereich dieses Fensters verläßt. Es würde ansonsten nicht mehr zu einer aktiven Session gehören und Lazy Loading würde nicht mehr funktionieren.

Wie kommen dann Objekte von einem Fenster ins nächste? Wie bei einer Webanwendung: Es wird die Id übergeben und über Session.get() wird das Objekt neu geladen. Ein Second Level Cache sorgt dafür, dass dieses neu Laden nur dann auf die Datenbank trifft, wenn der ursprüngliche Ladeprozess schon entsprechend lange her ist.

Bleibt die Frage, wie ich sicherstellen kann, dass keine veralteten Daten in einem Fenster dargestellt werden, obwohl sie in einem anderen Fenster der Anwendung schon in einer aktualisierten Form dargestellt sind. Dies kann durch erstaunlich wenig Aufwand bewerkstelligt werden. Mit einem Listener an postLoad, postUpdate und commit Events bekommen wir mit, wenn es neue Versionen einer Entität in der Datenbank gibt. Ãœber Sessions.getSessionStatistics können wir herausfinden, welche Session welche dieser Entitäten ebenfalls beeinhaltet und können gegebenenfalls Session.refresh() aufrufen, um das Objekt zu aktualisieren. Dies sollte natürlich nur für Sessions geschehen, die nur für Lesezwecke verwendet werden, da der Benutzer wohl kaum begeistert ist, wenn während er ein Objekt editiert, dies gerade mal unaufgefordert durch eine frische Version aus der Datenbank ersetzt wird.

Andere Quellen zu diesem Thema (mit vielen verschiedenen Meinungen zum Thema):

Talks

Wan't to meet me in person to tell me how stupid I am? You can find me at the following events: