Blog

Hibernate Sitzungsverwaltung

Maarten Winkels

Aktualisiert Oktober 23, 2025
5 Minuten
Als ich vor einiger Zeit einen JBoss Hibernate-Kurs besuchte, fiel mir auf, wie sehr die Sitzungsverwaltung (neben vielen anderen Dingen) zwischen Hibernate 2 und 3 gereift ist. Die Sitzungsverwaltung war früher eine "Client-Angelegenheit". Hibernate konnte einem Client nicht dabei helfen, seine eigenen Sitzungen zu verwalten, da sich das Wie und Wann der Sitzungsinitialisierung und -bereinigung zu sehr von Anwendungsfall zu Anwendungsfall unterschied. Der Standardansatz im Tutorial und anderen Lernmaterialien, auch für diesen Kurs für Fortgeschrittene, hat die Form von:

versuchen {
  session = sessionFactory.openSession();
  tx = session.beginTransaction();
  // Arbeit erledigen
  tx.commit();
} catch (HibernateException e) {
  tx.rollback();
} finally {
  if (Sitzung != null)
  session.close();
}
Obwohl dieser Code zu dem hässlichsten Spaghetti-Code mit starker Bindung an Hibernate führt, den man sich selbst in den kleinsten Projekten vorstellen kann, scheint die Motivation darin zu liegen, dass, sobald eine Entität in die Sitzung geladen ist, das einfache Durchlaufen von Assoziationen zu anderen Entitäten oder Sammlungen diese ebenfalls lädt und so die Sitzung vollständig vor dem Anwendungscode verbirgt. Die notwendigen "Root"-Objekte könnten im Voraus geladen werden, so dass der Anwendungscode so arbeiten kann, als ob sich alle Objekte immer im Speicher befänden. Der Nachteil ist, dass die Anwendung, sobald sie eine Entität benötigt, die nicht über bereits geladene Objekte erreichbar ist, oder eine Entität speichern muss, die nicht mit persistenten Entitäten verbunden ist, eine Sitzung benötigt. Es ist zwar möglich, eine neue Sitzung zu öffnen, aber die Arbeit mit Entitäten aus verschiedenen Sitzungen führt zu erheblichen Kopfschmerzen! Die offizielle Lösung bestand darin, die Sitzung in einem ThreadLocal zu speichern. Obwohl dies die offiziell empfohlene Lösung war, gab es in den Hibernate-Distributionen keine Unterstützung für diese Lösung. Jeder Hibernate-Benutzer müsste diese Lösung immer wieder neu implementieren. Man könnte den gleichen Ansatz wie zuvor verwenden, indem man die Sitzung in einem ThreadLocal speichert und sie abruft, um sie anschließend in einem finally-Block zu schließen. Dieser Ansatz funktioniert recht gut, solange Sie das Codewirrwarr ertragen können und kein fortgeschrittenes Transaktionsmanagement benötigen. Die Hibernate-Dokumentation weist auf die Verwendung von Interception zum Öffnen und Schließen Ihrer Sitzungen hin, aber auch dafür gibt es keine Unterstützung. Bei der Verwendung dieses Ansatzes ist Vorsicht geboten. Wenn der Interceptor im Kontext einer abgefangenen Methode gefunden wird, kann das Öffnen einer neuen Sitzung zu NonUniqueObjectExceptions führen, wenn die impliziten Sitzungsgrenzen überschritten werden. Das Starten einer neuen Transaktion führt zu einem noch weniger definierten Verhalten, da Hibernate gerne versucht, eine weitere Transaktion auf der zugrundeliegenden JDBC-Verbindung zu öffnen und verschachtelte Transaktionen von der JEE-Spezifikation offiziell nicht unterstützt werden. Wenn Sie einen Interceptor auf diese Weise verwenden, sollte die Anwendung dem Interceptor genügend Informationen zur Verfügung stellen, um zu bestimmen, wie er sich richtig verhalten soll. Soll er eine neue Sitzung unabhängig von einer bestehenden Sitzung öffnen? Soll er die vorhandene Transaktion verwenden und die Sitzung möglicherweise aus einem ThreadLocal abrufen? Soll es sich überhaupt um die Transaktionsverwaltung kümmern oder dies dem Anwendungscode überlassen? Sollte es die Sitzung schließen, nachdem die abgefangene Methode zurückgekehrt ist? Die Hibernate-Dokumente geben keinen Einblick in diesen Problembereich. Ab Hibernate 3.1 wird eine neue Strategie unterstützt. Sitzungen können mit Hilfe der Methode getCurrentSession() einer SessionFactory an den aktuellen Transaktionskontext gebunden werden. In einem JTA-Kontext funktioniert dies perfekt und der obige Code könnte einfach vereinfacht werden:
  session = sessionFactory.getCurrentSession();
  //Arbeiten
Der Transaktionskontext wird bereits durch die vom Container verwaltete JTA-Transaktion festgelegt, so dass sich der Anwendungscode nicht um den Start und das Commit oder Rollback der Transaktion kümmern muss. Da sich Hibernate hier auf die Fähigkeiten des JEE-Containers verlässt, werden Transaktionen deklarativ abgegrenzt und die Aussetzung von Transaktionen wird unterstützt. Eines der Probleme bei diesem Ansatz ist, dass der Code ohne einen JTA-Container nicht funktionieren wird. Glücklicherweise kann JBoss auch in JUnit-Tests als leichtgewichtiger Container verwendet werden. Wenn JBoss nicht der Anwendungsserver Ihrer Wahl ist, sind Sie wieder da, wo Sie angefangen haben, nämlich beim Öffnen und Schließen von Sitzungen. Als ich im Hibernate-Kurs saß und dem Dozenten zuhörte, wie er all diese neuen coolen Funktionen zusammenfasste, musste ich unweigerlich an Spring denken und daran, wie es all diese Funktionen und noch mehr unterstützt. Spring bindet eine Hibernate-Sitzung an den Transaktionskontext, sobald eine solche angefordert wird. Bei der Transaktionsübergabe wird die Sitzung automatisch geleert und geschlossen. Es ermöglicht das manuelle Öffnen und Schließen der Sitzung über seine statischen SessionFactoryUtils-Methoden, die nach der transaktionsgebundenen Sitzung suchen. Spring ermöglicht eine deklarative Transaktionsabgrenzung mit AOP, ohne von einem JTA-kompatiblen Container abhängig zu sein. Dem Anwendungscode kann es völlig gleichgültig sein, ob er in einem Container oder in einem JUnit-Test läuft, solange der Transaktionsmanager korrekt konfiguriert ist. Spring unterstützt auch transaktionslose Umgebungen. Wenn kein Transaktionskontext zugeordnet ist, öffnet der HibernateInterceptor eine neue Sitzung und schließt sie wieder, nachdem die abgefangene Methode entweder erfolgreich zurückgekehrt ist oder eine Exception ausgelöst hat. Seien Sie vorsichtig, wenn Sie mehrere Methoden abfangen und Objekte zwischen diesen Aufrufen freigeben. Diese Objekte werden zwischen den Aufrufen abgetrennt, da Spring die Sitzung eifrig schließt (nachdem sie geleert wurde). Für diese abgetrennten Objekte gilt die normale Semantik; um Lazy Loading oder (spätes) Flush beim Commit verwenden zu können, muss das Objekt wieder an die neue Session angehängt werden. session.lock(..) oder session.refresh(..) können hierfür verwendet werden. Der Umgang mit abgetrennten Objekten mit Hibernate ist allerdings eine ganz eigene Welt, die in der Regel sehr mühsam ist. In diesem Bereich scheint Spring wirklich alle möglichen Anwendungsfälle abzudecken und ist sehr einfach anzuwenden. Ich würde unter allen Umständen empfehlen, Spring für die Sitzungsverwaltung von Hibernate zu verwenden, unabhängig davon, wie Hibernate im Rest der Anwendung eingesetzt wird. Da die Zeit knapp war und selbst die offensichtlichste Verwendung von Hibernate in dem zweitägigen Kurs behandelt werden musste, kam ich nicht dazu, diese Meinung mit dem Dozenten und meinen Kollegen zu teilen. Er würde wahrscheinlich mit einem raffinierten, tiefgründigen Argument aufwarten, warum die Verwendung von HibernateTemplate in meiner Anwendung zu hoffnungslos fehlerhaftem Code und fehlerhafter Transaktionsbehandlung führen würde, aber das würde immer noch nicht meine Anforderungen an die Sitzungsbehandlung erfüllen.

Verfasst von

Maarten Winkels

Contact

Let’s discuss how we can support your journey.