Hibernate Sessions in Fat Client Anwendungen

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):

This entry was written by Jens Schauder , posted on Monday December 17 2007at 08:12 pm , filed under Java . Bookmark the permalink . Post a comment below or leave a trackback: Trackback URL.

5 Responses to “Hibernate Sessions in Fat Client Anwendungen”

  • Niels Hoffmann says:

    Hallom

    auch wenn dieser Beitrag schon ein bisschen älter ist, scheint das Thema an sich nicht alt zu werden.
    Wir beschäftigen uns gerade mit der selben Fragestellung. Rein intuitiv und nach allem was ich bis jetzt zu Hibernate gelesen habe würde ich den selben Ansatz verfolgen den Du in Deiner Antwort präsentierst.

    Da der Artikel ja schon ein bisschen älter ist, hoffe ich das Du jetzt um einige Erfahrungen reicher bist, was die Thematik angeht. Mich würde vor allem interessieren, ob der Weg zum Ziel führt und über welche Steine man dabei stolpern kann.

    Viele Grüße
    Niels Hoffmann

  • Hi Niels,
    der Weg führt zum Ziel, Steine zum stolpern gibt es allerdings jede Menge.

    - Die Lösung ist durchaus komplex. Es gab bei uns immer wieder Situationen, in denen jemand auf die falsche Session zugegriffen hat und dadurch Probleme verursacht wurden. Mögliche Abhilfe könnte hier ein DI Framework (hat jemand Spring gesagt?) schaffen, dass immer die Richtige Session injiziert. Da muss ich aber selbst erst noch schlau machen, ob es das hält, was ich mir davon verspreche.
    - Caching ist ein echter Schwachpunkt jeder 2 Schichtarchitektur. Jede Form des cachens beim Client kann zu veralteten Daten führen -> Optimistic locking exceptions

    Wenn man Probleme vermeiden will ist as Wichtigste, dass die Entwickler, oder zumindest ein wesentlicher Anteil, Hibernate wirklich verstanden haben. Und damit meine ich nicht, dass sie alle möglichen Mappings beherrschen, sondern das klar ist, wann man aus welchem Grund, update, load, get, flush oder commit aufruft und welche Auswirkungen das hat.

    Und man sollte sich der Alternativen bewusst sein:
    - Ein Webapp ähnlicher Ansatz, bei dem mit detachten Objekten gearbeitet wird. Sessions würden dabei immer nur kurz geöffnet, die Daten gelesen und/oder geschrieben und dann die Session wieder geschlossen werden. Vorteil: einfacher, vermutlich skalierbarer. Nachteil: kein Lazy Loading, was wiederum dazu führt, dass die Datenzugriffsschicht genau wissen muss was die GUI braucht. Also muss die GUI so schlau werden, dass sie dass der Datenzugriffsschicht sagen kann, was sie braucht, oder die Datenzugriffsschicht braucht implizites Wissen über die GUI.
    - 3 Schichtarchitektur. Vor und Nachteile wie oben, dazu kommt: Vorteil einfacher Umbau zur Webapp, besser cache Möglichkeit. Nachteil: komplexer in der Entwicklung und beim debuggen.

  • Hallo Jens,

    vielen Dank für die ausführliche Antwort und schön zu hören, dass der Ansatz im Grunde funktionoiert.

    Viele Grüße
    Niels Hoffmann

  • Michael Grünewald says:

    Hallo Jens,

    was hat Dein Spring-Ansatz gebracht?

    Viele Grüße Michae

  • Hallo Michael,
    ich bin leider noch nicht dazu gekommen mich ernsthaft mit Spring zu beschäftigen.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>