# @Transactional: Verschachtelte Methoden



## haf_blade (18. Feb 2011)

Hallöle liebe Leute,

ich hab eine kleine Frage zu Transaktionen in Spring.
Ich schreibe zur Zeit eine Webanwendung, in welcher man Artikel auf Aufträge und auf Rechnungen buchen kann.

Das Buchen eines Artikels soll dabei auch den Bestand des Artikels verändern.
So weit, so gut. Man hätte also drei Methoden die in etwa so aussehen:


```
@Transactional(propagation=Propagation.REQUIRED)
public void erstelleArtikelFuerAuftrag( Auftrag auftrag, Artikel artikel ) {

  //hier wird ein Artikel für einen Auftrag erstellt
  ...
  
  //hier wird die Bestandsänderungsmethode aufgerufen
  aendereBestand( artikel );

}

@Transactional(propagation=Propagation.REQUIRED)
public void erstelleArtikelFuerRechnung( Rechnung rechnung, Artikel artikel ) {

  //hier wird ein Artikel für eine Rechnung erstellt
  ...
  
  //hier wird die Bestandsänderungsmethode aufgerufen
  aendereBestand( artikel );

}

@Transactional(propagation=Propagation.REQUIRED)
public void aendereBestand( Artikel artikel ) {
  
  //hier wird der Bestand dann tatsächlich verändert
  ...

}
```

Der Quellcode hier ist vollkommen fiktiv aber es geht mir um folgendes:
Es gibt nun zwei Funktionen welche etwas sehr ähnliches tun. 
Die erste Methode "erstelleArtikelFuerAuftrag" erstellt einen Auftragsartikel und verändert danach den Bestand und die zweite Methode "erstelleArtikelFuerRechnung" erstellt einen Rechnungsartikel und verändert danach den Bestand.

Beide Methoden verwenden also die Methode "aendereBestand".
Es sollte nun so sein, dass diese Methode nie mehrmals gleichzeitig aufgerufen wird, da es sonst zu Fehlern im Bestand kommen könnte.

Nun zu meiner Frage: "Ist das durch @Transactional bei der Methode aendereBestand gegeben?"

Ich tue mir sehr schwer das zu testen aber vermute, dass das NICHT der Fall ist.
Denn:
Die Transaktionen werden bereits beim Aufruf der Methoden "erstelleArtikelFuerAuftrag" und "erstelleArtikelFuerRechnung" erstellt. Beim Aufruf der Transaktion "aendereBestand" wird KEINE NEUE Transaktion erstellt, sondern die alte weiter verwendet. Ich habe aber leider keine Ahnung ob sich diese beiden Transaktionen beim Aufrufen von "aendereBestand" als Lock erkennen.

Weiß das jemand?

Ich habe etwas im Internet gesucht und dachte zuerst ich könnte propagation.REQUIRES_NEW verwenden jedoch ist die Gesamttransaktion dann nicht mehr atomar. Es wird nämlich dann eine neue Transaktion erstellt und die alte wird suspended. Sollte nun die "innere" Transaktion fehlschlagen wird die darüber liegende keinen Rollback erfahren.

Was meint ihr?

Macht es Sinn in der aendereBestand Methode mit "propagatio.REQUIRES_NEW" zu markieren, einfach eine Exception zu werfen, falls was schief geht, damit einen Rollback zu erzwingen und dann die Exception in der Ober-Methode abzufangen und dort auch einen Rollback zu erzwingen?

Bin mir echt nicht sicher.

Vor allem wegen folgenden Aussagen:

1) Spring Transaktionen Beschreibung (Zitat aus Abschnitt: 10.5.6.1)


> Currently you cannot have explicit control over the name of a transaction, where 'name' means the transaction name that will be shown in a transaction monitor, if applicable (for example, WebLogic's transaction monitor), and in logging output. For declarative transactions, the transaction name is always the fully-qualified class name + "." + method name of the transactionally-advised class. For example, if the handlePayment(..) method of the BusinessService class started a transaction, the name of the transaction would be: com.foo.BusinessService.handlePayment.



Unter 10.5.7 findet man dann auch die Beschreibung der verschiedenen Propagation Werte.

2) IBM Blog zu Spring Transaktionen (Zitat aus Abschnitt: "REQUIRES_NEW transaction attribute pitfalls")



> Listing 11. Using the REQUIRES_NEW transaction attribute
> 
> ```
> @Transactional(propagation=Propagation.REQUIRES_NEW)
> ...




Könnt ihr mir Erleuchtung bringen? 

lg


----------



## FArt (21. Feb 2011)

Das richtige Flag wäre required, denn du möchtest ja, dass alles in einer Transaktion geschieht. Eine Transaktion sperrt nichts, regelt aber die Sichtbarkeit von geänderten Werten. 
Es kommt also darauf an, welches Transaktionsisolationslevel deine Datenbank aufweist.
Wenn du das gewünschte Verhalten nicht mehr mit der erforderlichen Performance erreichen kannst (read-commited, repeated read, serializable), solltest du dir über explizites Locking Gedanken machen, aber nicht unbeding vorher.


----------



## haf_blade (3. Mrz 2011)

Zuerst mal sorry, dass ich mich jetzt erst wieder melde aber es ging leider nicht früher.

Hab jetzt mal noch ein bißchen rumgelesen und hab jetzt nochmal eine Frage.

Wenn ich als Level "serializable" nehme und zwei Transaktionen habe die zuerst lesen, die Daten verändern und dann wieder schreiben. Wartet dann das System darauf dass eine Transaktion fertig ist bevor die zweite anfängt oder wie wird so etwas gehandhabt? Oder ist mit Fehlermeldungen zu rechnen, wenn gleichzeitiges Ausführen der Transaktionen auftritt?

Tue mir da echt schwerer mit als erwartet... tztztz...

Locks benutze ich zurzeit auch schon aber nur um Benutzer daran zu hindern ganz spezielle Funktionalitäten gleichzeitig zu benutzen, die organisatorisch schon Probleme machen würden.


----------



## FArt (4. Mrz 2011)

haf_blade hat gesagt.:


> Wenn ich als Level "serializable" nehme und zwei Transaktionen habe die zuerst lesen, die Daten verändern und dann wieder schreiben. Wartet dann das System darauf dass eine Transaktion fertig ist bevor die zweite anfängt oder wie wird so etwas gehandhabt? Oder ist mit Fehlermeldungen zu rechnen, wenn gleichzeitiges Ausführen der Transaktionen auftritt?



Das hängt von der DB Implementierung ab. Das Transaktionsisolationslevel sagt nur über die Sichtbarkeit von Daten und deren Konsistenz etwas aus. Wie das realisiert ist (z.B. ein Blockieren an irgend einer Stelle) hängt von der DB ab.

Ganz gut ist es hier erklärt: Isolation (Datenbank) ? Wikipedia

Schau nach, was die Doku deiner DB dazu sagt.


----------

