wake-up-neo.com

Verschachtelte Transaktionen im Frühling

In meinem Spring Boot-Projekt habe ich folgende Servicemethode implementiert:

@Transactional
public boolean validateBoard(Board board) {
    boolean result = false;
    if (inProgress(board)) {
        if (!canPlayWithCurrentBoard(board)) {
            update(board, new Date(), Board.AFK);
            throw new InvalidStateException(ErrorMessage.BOARD_TIMEOUT_REACHED);
        }
        if (!canSelectCards(board)) {
            update(board, new Date(), Board.COMPLETED);
            throw new InvalidStateException(ErrorMessage.ALL_BOARD_CARDS_ALREADY_SELECTED);
        }
        result = true;
    }
    return result;
}

innerhalb dieser Methode verwende ich eine andere Servicemethode, die als update bezeichnet wird:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public Board update(Board board, Date finishedDate, Integer status) {
    board.setStatus(status);
    board.setFinishedDate(finishedDate);

    return boardRepository.save(board);
}

Ich muss Änderungen an der Datenbank in der update-Methode festschreiben, unabhängig von der Besitzertransaktion, die in der validateBoard-Methode gestartet wird. Im Moment werden Änderungen in Ausnahmefällen rückgängig gemacht.

Selbst mit @Transactional(propagation = Propagation.REQUIRES_NEW) funktioniert es nicht.

Wie mache ich das richtig mit Spring und erlaube geschachtelte Transaktionen?

18
alexanoid

Diese Dokumentation behandelt Ihr Problem - https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-annotations

Im Proxy-Modus (der Standardeinstellung) werden nur externe Methodenaufrufe abgefangen, die über den Proxy eingehen. Dies bedeutet, dass der Selbstaufruf, dh eine Methode innerhalb des Zielobjekts, die eine andere Methode des Zielobjekts aufruft, zur Laufzeit nicht zu einer tatsächlichen Transaktion führt, selbst wenn die aufgerufene Methode mit @Transactional markiert ist. Der Proxy muss außerdem vollständig initialisiert werden, um das erwartete Verhalten bereitzustellen. Sie sollten sich also nicht auf diese Funktion in Ihrem Initialisierungscode verlassen, d. H. Auf @PostConstruct.

Es besteht jedoch die Möglichkeit, in den AspectJ-Modus zu wechseln

18
Jakub Bibro

Die Verwendung eines "Self" -Injektionspartners kann dieses Problem beheben.

beispielcode wie folgt:

@Service @Transactional
public class YourService {
   ... your member

   @Autowired
   private YourService self;   //inject proxy as instance member variable ;

   @Transactional(propagation= Propagation.REQUIRES_NEW)
   public void methodFoo() {
      //...
   }

   public void methodBar() {
      //call self.methodFoo() rather than this.methodFoo()
      self.methodFoo();
   }
}

Der Punkt ist eher "Selbst" als "Dies".

3
shenyu1997

Die grundlegende Daumenregel in Bezug auf verschachtelte Transaktionen ist, dass sie vollständig von der zugrunde liegenden Datenbank abhängig sind, dh, die Unterstützung für geschachtelte Transaktionen und deren Behandlung ist datenbankabhängig und hängt von ihr ab Wird von der 'Host'-Transaktion nicht gesehen, bis die geschachtelte Transaktion festgeschrieben ist. Dies kann durch die Transaktionsisolation in @Transactional erreicht werden (isolation = "").

Sie müssen die Stelle im Code angeben, von der eine Ausnahme ausgelöst wird, d. H. Von der übergeordneten Methode: "validateBoard" oder von der untergeordneten Methode: "Update".

Ihr Code-Snippet zeigt, dass Sie die Ausnahmen explizit auslösen.

DU MUSST WISSEN::

In seiner Standardkonfiguration wird die Transaktion Spring Framework Der Infrastrukturcode kennzeichnet nur eine Transaktion für das Rollback im Fall der Laufzeit, ungeprüfte Ausnahmen; das ist, wenn die geworfene Ausnahme .__ ist. eine Instanz oder Unterklasse von RuntimeException.

@Transactional setzt jedoch niemals eine Transaktion für eine geprüfte Ausnahme zurück.

So können Sie in Spring definieren

  • Ausnahme, für die die Transaktion rückgängig gemacht werden soll
  • Ausnahme für die Transaktion sollte nicht zurückgesetzt werden

Versuchen Sie, Ihre untergeordnete Methode mit Anmerkungen zu versehen: Aktualisieren Sie mit @Transactional (no-rollback-for = "ExceptionName") oder Ihrer übergeordneten Methode.

1
Philip Dilip

Ihre Transaktionsanmerkung in der update-Methode wird von der Spring-Transaktionsinfrastruktur nicht berücksichtigt, wenn sie von einer Methode derselben Klasse aufgerufen wird. Weitere Informationen zur Funktionsweise der Spring-Transaktionsinfrastruktur finden Sie unter this .

1
Shailendra

Ihr Problem ist der Aufruf einer Methode von einer anderen Methode innerhalb desselben Proxy-Servers. Sie wird selbst aufgerufen. In Ihrem Fall können Sie das Problem leicht beheben, ohne eine Methode innerhalb eines anderen Dienstes zu verschieben (warum müssen Sie einen anderen Dienst erstellen, nur um eine Methode von einem Dienst zu einem anderen zu verschieben, um Selbstaufruf zu vermeiden?), Um nur aufzurufen die zweite Methode nicht direkt aus der aktuellen Klasse, sondern aus Federbehälter. In diesem Fall rufen Sie die zweite Proxy-Methode mit der Transaktion und nicht mit der Eigeninvokation auf.

Dieses Prinzip ist nützlich für jedes Proxy-Objekt, wenn Sie einen Selbstaufruf benötigen, nicht nur einen Transaktions-Proxy.

@Service
class SomeService ..... {
    -->> @Autorired
    -->> private ApplicationContext context;
    -->> //or with implementing ApplicationContextAware

    @Transactional(any propagation , it's not important in this case)
    public boolean methodOne(SomeObject object) {
      .......
       -->> here you get a proxy from context and call a method from this proxy
       -->>context.getBean(SomeService.class).
            methodTwo(object);
      ......
   }

    @Transactional(any propagation , it's not important in this case)public boolean 
    methodTwo(SomeObject object) {
    .......
   }
}

wenn Sie context.getBean(SomeService.class).methodTwo(object); aufrufen, gibt Container ein Proxy-Objekt zurück. Auf diesem Proxy können Sie methodTwo(...) mit der Transaktion aufrufen.

0
xyz