Wenn ich im Frühjahr:
ServiceA.serviceA() -> ServiceB.serviceB() -> ServiceC.serviceC() ->ServiceD.serviceD()
Dabei kann ServiceD.serviceD()
eine Laufzeitausnahme auslösen: MyRuntimeException
, die wieder an ServiceA.serviceA
catch-Block weitergegeben wird. Spielt es eine Rolle, auf welchen Service ich @Transactional(noRollbackFor=[MyRuntimeException.class])
gesetzt habe?
Gibt es einen Unterschied zwischen der Bereitstellung eines Services?
Hinweis: Alle meine Dienste sind als @Transactional
gekennzeichnet Da Sie hier keine Genauigkeit angegeben haben, nehme ich an, dass Sie die Standard-Propagierung von PROPAGATION_REQUIRED
verwenden. In diesem Kontext verwenden die 4 Services die gleiche Transaktion. Wenn eine der drei inneren die Transaktion als schreibgeschützt markiert, erhält die äußere Instanz ein UnexpectedRollbackException
, um sie darüber zu informieren, dass das angeforderte Commit tatsächlich erfolgt führte zu einem Rollback. Von Spring Reference Manual: Wenn jedoch ein innerer Transaktionsbereich den Rollback-only-Marker festlegt, hat die äußere Transaktion nicht den Rollback selbst entschieden, und daher ist der Rollback (der vom inneren Transaktionsbereich automatisch ausgelöst wird) unerwartet. Eine entsprechende UnexpectedRollbackException wird an diesem Punkt ausgelöst. Dies ist das erwartete Verhalten, so dass der Aufrufer einer Transaktion niemals in die Irre geführt werden kann, anzunehmen, dass ein Commit ausgeführt wurde, obwohl dies nicht der Fall war. Wenn also eine innere Transaktion (die der äußere Aufrufer nicht kennt) stillschweigend eine Transaktion als Rollback-only markiert, ruft der äußere Aufrufer Commit auf. Der äußere Aufrufer muss eine UnexpectedRollbackException erhalten, um deutlich anzuzeigen, dass stattdessen ein Rollback ausgeführt wurde. . Und wenn die äußere Transaktion aufgrund der Ausnahme entscheidet, die Transaktion zurückzusetzen, wird die Transaktion offensichtlich zurückgesetzt.
Wenn also keiner der Dienste die Ausnahme abfängt und Sie eine Weitergabe von PROPAGATION_REQUIRED
verwenden, müssen mindestens die vier beteiligten Methoden mit @Transactional(noRollbackFor=[MyRuntimeException.class])
versehen werden.
Eine Alternative zur Verwendung von noRollbackFor=[MyRuntimeException.class]
wäre das Abfangen der Ausnahme in der entsprechenden Methode von ServiceD
. In diesem Fall wird die Ausnahme niemals den Stapel hochklettern, und keiner der Transaktionsproxys wird jemals wissen, dass er aufgetreten ist. Das Commit würde dann normalerweise am Ende der Transaktion stattfinden.
Bearbeiten Sie pro Kommentar:
Wenn Sie bei der Ausnahmenverwaltung weitere Kontrolle wünschen, könnten Sie versuchen, die Methode zu duplizieren: eine Transaktionsmethode, die eine nicht-transaktionale Methode in Ihrer Serviceklasse aufruft. Und Sie könnten das nicht-transaktionale Konto aufrufen, wenn Sie keinen anderen Transaktions-Proxy in der Kette wünschen. Dies ist jedoch nur dann sinnvoll, wenn dieser Anwendungsfall (eine Serviceklasse, die eine andere Serviceklasse mit spezieller Ausnahmeanforderung aufruft) außergewöhnlich ist.
Alternativ können Sie die Implementierungen auch in die anderen Serviceklassen einfügen, anstatt die Transaktionsproxys ( @Autowired private ServiceBImpl serviceB;
) zu injizieren. Da Sie bereits eine Transaktion auf der äußeren Ebene erhalten haben, sollten alle DAO-Operationen in Ordnung sein. Da es auf der äußeren Ebene nur einen Transaktions-Proxy gibt, haben Sie einen einzigen Kontrollpunkt für die Ausnahmenverwaltung. Es ist eher unüblich, Klassen anstelle von Interfaces zu injizieren, und Sie sollten das warum in einer rot blinkenden Schrift dokumentieren :-), aber es sollte Ihren Anforderungen entsprechen.
Tags und Links java spring spring-transactions