@Transactional: Verschachtelte Methoden

haf_blade

Mitglied
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:

Java:
@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
Java:
@Transactional(propagation=Propagation.REQUIRES_NEW)
public long insertTrade(TradeData trade) throws Exception {...}

@Transactional(propagation=Propagation.REQUIRES_NEW)
public void updateAcct(TradeData trade) throws Exception {...}

Notice in Listing 11 that both of these methods are public, implying that they can be invoked independently from each other. Problems occur with the REQUIRES_NEW attribute when methods using it are invoked within the same logical unit of work via inter-service communication or through orchestration. For example, suppose in Listing 11 that you can invoke the updateAcct() method independently of any other method in some use cases, but there's also the case where the updateAcct() method is also invoked in the insertTrade() method. Now, if an exception occurs after the updateAcct() method call, the trade order would be rolled back, but the account updates would be committed to the database, as shown in Listing 12:

Listing 12. Multiple updates using the REQUIRES_NEW transaction attribute
Java:
@Transactional(propagation=Propagation.REQUIRES_NEW)
public long insertTrade(TradeData trade) throws Exception {
   em.persist(trade);
   updateAcct(trade);
   //exception occurs here! Trade rolled back but account update is not!
   ...
}

This happens because a new transaction is started in the updateAcct() method, so that transaction commits once the updateAcct() method ends. When you use the REQUIRES_NEW transaction attribute, if an existing transaction context is present, the current transaction is suspended and a new transaction started. Once that method ends, the new transaction commits and the original transaction resumes.


Könnt ihr mir Erleuchtung bringen? :D

lg
 

FArt

Top Contributor
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

Mitglied
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

Top Contributor
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.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
D Duplizieren Services DAO-Methoden Application Tier 31

Ähnliche Java Themen

Neue Themen


Oben