pinske.eu

Alexander Pinske

Follow me on GitHub

Hibernate & Websphere Application Server

Wer Hibernate im Websphere Application Server (WAS) einsetzen will, wird früher oder später über die Frage der JtaPlatform stolpern. Hibernate liefert zwei Implementierungen für WAS mit. Welche soll man denn nun einsetzen? Kurze Antwort: Am besten keine der beiden.

Aber von vorn. Wozu benötigt Hibernate diese JtaPlatform? JTA ist ein Java Standard, der es EE-Containern erlaubt über eine einheitliche Schnittstelle mit XA-Transaction Managern zu kommunizieren. Solange man also keine XA Transaktionen benötigt, reicht es aus Hibernate mit Standard JDBC Transaktionen zu betrieben.

Die JtaPlatform von Hibernate ist eine Abstraktion, über die Hibernate sich in die bestehenden Container-Transaktionen integrieren will. Die JtaPlatform ist als Lookup auf einen javax.transaction.TransactionManager konzipiert. Hier gehen die Probleme auch schon los. Die JTA Spec schreibt nicht vor, dass ein EE-Container den Zugang zu einem solchen TransactionManager vorsehen muss. Dieses Interface sollte eigentlich als SPI, also Contract zwischen Container und Transaktionsmanager, gesehen werden. Viele EE-Container, darunter JBoss, sehen im Rahmen ihrer Schnittstellen den Zugriff auf den TransactionManager vor. Diese Container machen es Hibernate leicht. Hier besteht die JtaPlatform essentiell nur aus einem entsprechenden JNDI-Lookup. Websphere Application Server bietet diesen Zugang zu einem Objekt, welches TransactionManager implementiert, nicht (mehr) an, sondern stattdessen den com.ibm.[...].UOWTransactionManager. Dies wird von einigen als unsinnig und proprietär empfunden, ist aber genau so proprietär (EE-Sprech: non-portable) wie der Zugriff im JBoss auf den TransactionManager, selbst wenn dies ein javax-Interface ist.

Bis WAS 5 gab es einen Weg um an den TransactionManager zu gelangen, der von der “normalen” org.hibernate. [...].WebsphereJtaPlatform benutzt wird. Dieser Weg ist allerdings nicht mehr supportet. Daher gibt es die WebsphereExtendedJtaPlatform, die versucht über den UOWTransactionManager die Funktionalität des javax.transaction.TransactionManager nachzubilden. Dies gelingt leider nicht ganz, z.B. ist die Methode setRollbackOnly() nicht implementiert, die benutzt wird, wenn Hibernate z.B. eine OptimisticLock-Situation erkennt und die aktuelle TX JPA-Spec-konform zurückrollen möchte. Mit der ExtendedJtaPlatform fliegt hier eine UnsupportedOperationException.

Der Code der ExtendedJtaPlatform zeugt zudem von Unwissen und Ignoranz. So wurde speziell die Hibernate-Abstraktion erweitert (Details: getTransactionIdentifier), weil der WAS “not a sane JEE/JTA container” sei, das eigentliche Problem allerdings vom Entwicker einige Zeilen weiter selbst verursacht wurde (Details: Es wird immer eine neue Instanz des Transaction-Proxys erzeugt).

Hier passt ein Zitat, über das ich letztens gestolpert bin:

Someone, somewhere, coded himself into a corner and decided that […] was to blame. I hear this a lot. [Quelle]

Glücklicherweise gibt es seit JTA 1.1 eine Lösung für genau dieses Problem: das etwas irreführend benannte Interface TransactionSynchronizationRegistry (TSR). Ein konformer Container muss ein Objekt diesen Typs per definiertem JNDI Namen zur Verfügung stellen. Die TSR bietet Möglichkeiten mit der aktuell laufenden XA-TX zu interagieren und wurde speziell für Bedürfnisse von Frameworks wie Hibernate in die Spec aufgenommen. Hibernate könnte jetzt einfach diesen Weg gehen und (fast) das ganze Container-Transaktion-Integrationsthema lösen. Es gibt in Mailinglist und Bug-Tracker auch bereits Bestrebungen, aber noch nichts konkretes, obwohl JTA 1.1 bereits recht lange verfügbar ist. Eines der Probleme ist, dass die JtaPlatform, obwohl in letzter Zeit oft Reafactoring unterlegen (leider nur renames und moves), eine sehr “leaky abstraction” (statt weniger sauberer Methoden, die genau definieren, was mit einer TX möglich sein soll, werden einfach einige Objekte herausgegeben, über deren Verwendung dann der Aufrufer selbst entscheiden muss) ist und der verwendende Code nicht sauber strukturiert ist.

Für ein Projekt haben wir eine eigene WebsphereJtaPlatform gebaut, die über die TSR mit dem TX-Mgr interagiert und damit (fast) platformunabhängig die Brücke zwischen Hibernate und XA schlägt. Trotzdem hat unsere neue JtaPlatform weiterhin Websphere im Namen. Warum? Es kann leider durch die Verwendung der TSR immer noch nicht für alle Anwendungsfälle völlige Platformunabhängigkeit erreicht werden. Dies ist relativ sicher auch der Grund warum Spring seinen PlatformTransactionManager für WAS auch mit Hilfe der IBM-UOW-API implementiert (und dafür auch explizit IBM-Anerkennung genießt [Quelle]), da Spring ein wesentlich breiteres Spektrum an Anwendungsfällen abzudecken hat, als die klar umrissenen Fälle von Hibernate. Doch auch in Hibernate gibt es einige wenige Fälle, für die die TSR nicht mehr ausreicht. Über die TSR kann in der aktuellen Version nur mit der aktuell laufenden TX interagiert werden. Für einige Anwendungsfälle ist es aber nötig die aktuelle TX zu suspenden und eine neue TX zu starten (TxAttr “requires new”). In Hibernate ist dafür u.a. der Isolation-Delegate (lies: Statements in einer neuen TX von der bestehenden TX isolieren, z.B. damit diese sofort in der DB sichtbar werden) zuständig, um z.B. table-generated ids zu erzeugen.

Da wir Identity-Ids benutzen reicht uns die vorher beschriebene Integration über die TSR. Ein noch nicht implementiertes Feature (weil nicht verwendet) ist “getCurrentSession”-Support, der über die TSR aber einfach zu realisieren wäre.

key take-away points:

  • Hibernate Code ist messy
  • Websphere ist nicht schuld am Untergang der Welt
  • Es lohnt sich immer mal wieder ein paar JSRs zu lesen. Man lernt nie aus.
  • Es lohnt sich zu schauen, wie Spring die Dinge angeht.
  • Spring Code ist schön.
  • Es lohnt sich Dinge zu hinterfragen.