Transaktionssteuerung mit @Transactional

Transaktionen sind nötig, um Daten atomar, konsistent, isoliert und dauerhaft bearbeiten und speichern zu können (ACID-Prinzip). Methoden von CDI-Beans können durch eine simple Annotation mit einem aktiven Transaktionskontext versehen werden.

Transaktionsinterceptor mit dem Binding @Transactional

Schon seit der Version 1.1 von CDI steht ein global aktivierter Transaktionsinterceptor zur Verfügung. Er wird mit der Annotation @Transactional einer Methode einer CDI-Bean zugeordnet:

public class SomeService {
  ...
  @Transactional
  public void doSomethingTransactionally() {
    ...
  }

Beim Aufruf der Methode ohne aktive Transaktion wird nun eine Transaktion gestartet und nach dem Verlassen der Methode wieder geschlossen. Die Regeln für das Transaktionsende sind etwas seltsam:

  • Endet die Methode mit einem Return, wird ein Commit versucht.
  • Wirft die Methode eine sog. System Exception, resultiert dies in einem Rollback. System Exceptions sind i. W. die unchecked Exceptions (ergänzt um die RemoteException, die aber explizit kaum eine Rolle spielt).
  • Wirft die Methode eine Application Exception – das sind alle anderen Exeptions – wird ein Commit (!) versucht.

Insbesondere der letzte Punkt ist (zumindest mir) unverständlich: Für mich modellieren Exceptions immer Ausnahmesituationen, die den jeweiligen Geschäftsprozess ungültig machen. Glücklicherweise kann man mit einem Parameter der Annotation auch in diesem Fall ein Rollback ansteuern:

  @Transactional(rollbackOn=Exception.class)
  public void doSomethingTransactionally() {

Transaktionsmodus

Die Art der Transaktionssteuerung durch @Transactional kann mit dem Parameter value bestimmt werden:

  • TxType.REQUIRED ist der Default, der in den allermeisten Fällen passend ist. Beim Aufruf einer davon betroffenen Methode wird geprüft, ob bereits eine aktive Transaktion vorliegt. Wenn ja, wird diese für den Methodenaufruf genutzt. Andernfalls wird eine neue Transaktion gestartet und am Methodenende wieder beendet.
  • TxType.REQUIRES_NEW startet bei Methodenbeginn stets eine neue Transaktion, die am Methodenende wieder beendet wird. Eine ggf. bereits aktive Transaktion wird zuvor suspendiert. Dieser Modus sollte dann verwendet werden, wenn die Ergebnisse des Methodenaufrufs auch dann dauerhaft abgespeichert werden sollen, wenn die umgebende Transaktoin zurückgerollt wird. Beispiel: Auditing/Protokollierung von Methodenaufrufen.
  • TxType.MANDATORY verlangt eine aktive Transaktion bei Methodenbeginn und wirft andernfalls eine TransactionRequiredException. Dieser Modus ist könnte prinzipiell für Repositories (a.k.a DAOs) interessant sein, da deren feingranulare Methoden i. A. von einem Service aufgerufen werden, der selbst transaktional ist. Leider werden Lifecycle-Methoden (@PostConstruct-annotierte Methoden etc.) nicht durch die @Transactional-Interceptoren intercepted. Häufig werden darin Repository-Methoden aufgerufen. Dann sollte für Repositories wie auch bei anderen Services TxType.REQUIRED verwendet werden.
  • Die weiteren Modi TxType.NOT_SUPPORTED, TxType.SUPPORTS und TxType.NEVER sind i. A. unbrauchbar.

Danke für’s Lesen und bis bald – vielleicht auch in einem unserer Trainings in Berlin, Bielefeld, Köln oder bei Ihnen!
https://gedoplan-it-training.de/

Werbeanzeigen