Oracle
 sql >> Datenbank >  >> RDS >> Oracle

A zurücksetzen, wenn B schief geht. Springboot, jdbctemplate

@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).