@Transactional
Annotation in Spring funktioniert, indem Sie Ihr Objekt in einen Proxy einschließen, der wiederum mit @Transactional
kommentierte Methoden umschließt bei einer Transaktion. Aus diesem Grund funktioniert die Annotation nicht mit privaten Methoden (wie in Ihrem Beispiel), da private Methoden nicht vererbt werden können => Sie können nicht umschlossen werden (dies trifft nicht zu, wenn Sie deklarative Transaktionen mit Aspektj verwenden, dann gelten die Proxy-bezogenen Vorbehalte unten nicht).
Hier ist eine grundlegende Erklärung, wie @Transactional
Frühlingszauber wirkt.
Du hast geschrieben:
class A {
@Transactional
public void method() {
}
}
Aber das bekommen Sie tatsächlich, wenn Sie eine Bohne injizieren:
class ProxiedA extends A {
private final A a;
public ProxiedA(A a) {
this.a = a;
}
@Override
public void method() {
try {
// open transaction ...
a.method();
// commit transaction
} catch (RuntimeException e) {
// rollback transaction
} catch (Exception e) {
// commit transaction
}
}
}
Dies hat Einschränkungen. Sie funktionieren nicht mit @PostConstruct
Methoden, da sie aufgerufen werden, bevor das Objekt weitergeleitet wird. Und selbst wenn Sie alles richtig konfiguriert haben, werden Transaktionen nur auf nicht aktiviert zurückgesetzt Ausnahmen standardmäßig. Verwenden Sie @Transactional(rollbackFor={CustomCheckedException.class})
wenn Sie ein Rollback für eine geprüfte Ausnahme benötigen.
Eine weitere häufig anzutreffende Einschränkung, die ich kenne:
@Transactional
Methode funktioniert nur, wenn Sie sie "von außen" aufrufen, im folgenden Beispiel b()
wird nicht in Transaktion eingeschlossen:
class X {
public void a() {
b();
}
@Transactional
public void b() {
}
}
Es liegt auch daran, dass @Transactional
funktioniert durch Proxying Ihres Objekts. Im obigen Beispiel a()
wird X.b()
aufrufen keine erweiterte "Spring-Proxy"-Methode b()
es kommt also zu keiner Transaktion. Als Workaround müssen Sie b()
aufrufen von einer anderen Bohne.
Wenn Sie auf einen dieser Vorbehalte gestoßen sind und eine vorgeschlagene Problemumgehung nicht verwenden können (Methode nicht privat machen oder b()
aufrufen von einer anderen Bean) können Sie TransactionTemplate
verwenden statt deklarative Transaktionen:
public class A {
@Autowired
TransactionTemplate transactionTemplate;
public void method() {
transactionTemplate.execute(status -> {
A();
B();
return null;
});
}
...
}
Aktualisieren
Beantwortung der aktualisierten OP-Frage mit den obigen Informationen.
Welche Methode soll mit @Transactional:changes() annotiert werden? databaseChanges()?
@Transactional(rollbackFor={Exception.class})
public void changes() throws Exception {
someLogicBefore();
databaseChanges();
someLogicAfter();
}
Stellen Sie sicher, dass changes()
wird "von außerhalb" einer Bean aufgerufen, nicht von der Klasse selbst und nachdem der Kontext instanziiert wurde (z. B. ist dies nicht afterPropertiesSet()
oder @PostConstruct
kommentierte Methode). Verstehen Sie, dass Spring-Rollback-Transaktionen standardmäßig nur für ungeprüfte Ausnahmen ausgeführt werden (versuchen Sie, in der Liste RollbackFor Checked Exceptions genauer zu sein).