Blog

Konfigurieren von Hibernate und Spring für JTA

Maarten Winkels

Aktualisiert Oktober 23, 2025
5 Minuten
Spring ist ein großartiges Framework für die Injektion von Abhängigkeiten und wird mit einer Vielzahl von Hilfsklassen und Dienstprogrammen für alle möglichen Dinge geliefert. Hibernate ist ein Persistenzdienst mit vielen nützlichen Funktionen, der relativ einfach zu verwenden ist. Die Konfiguration der beiden Frameworks ist nicht immer einfach. Sie zusammen zu konfigurieren ist manchmal schwierig und man kann leicht Fehler machen. Dieser Blog behandelt ein Problem in einer Konfiguration, die ziemlich häufig vorkommt: Verwenden Sie Spring für die Transaktionsverwaltung auf einem JTA-Anbieter und verwenden Sie Hibernate für die Persistenz. Die Transaktionsabgrenzung ist mit Spring einfach und deklarativ. Das Problem ist, dass Hibernate manchmal die aktuelle Transaktion erkennen muss und dies konfiguriert werden muss. Dies führt zu schwer zu erkennenden Fehlern in Anwendungen, die auf Auto-Flushing angewiesen sind. Auto Flushing Auto Flushing ist eine Funktion von Hibernate, die den In-Memory-Status mit dem Datenbankstatus synchronisiert, wenn eine Abfrage ausgeführt wird. Normalerweise synchronisiert Hibernate den In-Memory-Status und den Datenbankstatus nur, wenn die Sitzung geleert wird. Bei Spring kann dies so konfiguriert werden, dass es beim Commit der Transaktion geschieht. Dies kann zu unerwartetem Verhalten führen. Sehen Sie sich den folgenden Code an

  public void transactionInterceptedMethod() {
  Person p = new Person("Maarten");
  personDao.save(p);
  Liste Personen = personDao.findAll();
  }
Wird die neue Person auf der Liste der Personen stehen? Ich denke, jeder würde das vermuten. In der Tat hängt dies stark davon ab, wie Spring und Hibernate konfiguriert sind.
  1. Das Personenobjekt wird nicht sofort in die Datenbank eingefügt, wenn es Hibernate angeboten wird. Es wird im Speicher gehalten, bis die Sitzung geleert wird. (Es gibt Ausnahmen, aber dies ist die allgemeine Regel).
  2. Die findAll-Methode sucht in der Datenbank in derselben Transaktion wie die, die zum Einfügen des Personenobjekts verwendet wird. Dennoch findet sie die neue Person nur, wenn sie in derselben Transaktion vor der Abfrage in die Datenbank eingefügt wurde.
  3. Spring wird die Transaktion festschreiben und Hibernate anweisen, die aktuelle Sitzung erst am Ende der Methode zu löschen.
All diese Umstände beeinflussen das Ergebnis der findAll-Methode. Es scheint, dass wir das neue Personenobjekt nur dann in der Liste haben können, wenn wir die Sitzung zwischen den Methoden save und findAll flushen. Hier kommt das automatische Flushing ins Spiel. Wenn der Flush-Modus der Sitzung auf "AUTO" eingestellt ist, prüft Hibernate den Zustand im Speicher, um festzustellen, ob es irgendwelche Änderungen gibt, die das Ergebnis einer Abfrage beeinflussen könnten, die es gerade ausführt. Wenn Hibernate dies feststellt, wird die Sitzung geleert und der Zustand im Arbeitsspeicher mit dem Zustand der Datenbank synchronisiert. Dabei wird der Transaktionsstatus nicht verändert, sondern es werden lediglich einige SQL-Anweisungen im aktuellen Transaktionskontext ausgeführt. Das automatische Flushing ist recht intelligent, was wann geflusht werden soll, auch wenn einige Situationen schwer zu erkennen sind. Um die Leistung zu verbessern, flusht Hibernate nicht einfach immer alles, sondern betrachtet die Tabellen in der Datenbank, die von der Abfrage berührt werden, und die Daten im Speicher, die in diesen Tabellen gespeichert werden sollen. Wenn es keine Überschneidungen gibt, werden keine Daten persistiert. Wenn eine Überschneidung festgestellt wird, wird die gesamte Sitzung geleert, um die Konsistenz zu gewährleisten. Spring, JTA und Transaktionserkennung Das scheint alles gut zu funktionieren, aber was hat das mit Spring, JTA oder Transaktionserkennung zu tun? Nun, Hibernate führt nur dann ein automatisches Flushing durch, wenn es erkennt, dass eine Transaktion läuft. Es ist mir nicht ganz klar, warum es das Flushing nicht durchführt, aber so funktioniert es. Es gibt eine Reihe von Strategien, um zu erkennen, dass eine Transaktion stattfindet. Die Standardstrategie besteht darin, den eigenen Transaktionsmechanismus zu verwenden. Wenn Sie Spring verwenden, um Transaktionen zu verwalten, ist die Verwendung des Hibernates-Mechanismus für Transaktionen (der eine dünne Schicht über JDBC ist) eine Option. Dieser Mechanismus unterstützt keine verteilten Transaktionen. Um verteilte Transaktionen zu unterstützen, sagt Spring, dass Sie einfach ihren JtaTransactionManager einfügen können, um den HibernateTransactionManager zu ersetzen. Das ist jedoch nicht ganz richtig: Es wird nahtlos für die Transaktionsabgrenzung funktionieren, aber nicht für die Transaktionserkennung durch Hibernate. Die Transaktionsabgrenzung wird problemlos funktionieren: Spring wird eine neue Hibernate-Sitzung starten, sobald eine Transaktion gestartet wird. Hibernate wird mit dieser Sitzung arbeiten, ohne zu wissen, welche Transaktion geöffnet wurde. Dies ist kein Problem für die Abgrenzung: Spring wird die Sitzung leeren und schließen, wenn die Transaktion geschlossen wird. Für Hibernates Transaktionserkennung funktioniert das nicht: Hibernate weiß nichts von der von Spring gestarteten Transaktion und führt kein automatisches Flushing durch. Es gibt nicht viele Funktionen in Hibernate, die von der Transaktionserkennung abhängen, so dass dieses Fehlverhalten des Systems möglicherweise eine ganze Weile unbemerkt bleibt. Das Problem mit dem Auto-Flushing ist, dass es nicht immer so einfach ist wie zwei aufeinanderfolgende Zeilen, die ein Save und FindAll ausführen. Die Logik, die eine Entität speichert, und die Logik, die davon abhängt, dass die Entität durch eine Abfrage innerhalb derselben Transaktion gefunden wird, können sich in separaten Klassen befinden, die nur durch einen typischen Ablauf verbunden sind. Lösung Wie teilen wir also Hibernate mit, welche Strategie es für die Transaktionserkennung verwenden soll? Es gibt zwei Möglichkeiten: 1) Hibernate direkt konfigurieren oder 2) Hibernate über Spring konfigurieren.Um Hibernate direkt zu konfigurieren, verwenden Sie die Eigenschaften hibernate.transaction.manager_lookup_class und hibernate.transaction.factory_class. Diese Eigenschaften würden von Hibernate zum Starten von Transaktionen verwendet werden, aber wir starten keine Transaktionen über Hibernate, sondern erkennen sie nur. Um Hibernate über Spring zu konfigurieren, injizieren Sie einen JtaTransactionManager in die LocalSessionFactoryBean. Je nach der spezifischen Version von Spring, die Sie verwenden, müssen weitere Einstellungen geändert werden. Fazit Die Entwicklung mit leistungsstarken Frameworks wie Spring und Hibernate macht viele Aufgaben einfach. Es ist von großer Bedeutung, dass die Frameworks richtig konfiguriert sind, insbesondere wenn sie zusammenarbeiten müssen. Ich hoffe, dass dieser Blog Ihnen hilft, einen kleinen Fehler in Ihrer Konfiguration zu entdecken, der zu schwerwiegenden Problemen führen könnte.

Verfasst von

Maarten Winkels

Contact

Let’s discuss how we can support your journey.