2017-12-10 192 views
8

Istnieje jednostka:Wiosna danych: transakcja wycofywania na ponawiania

@Entity 
class A { 
    ... 
    @Version 
    int version; 
} 

A przypadki zmiana zaimplementowana w optymistyczny sposób:

@Transactional(rollbackFor = {StaleStateException.class}) 
@Retryable(value = {StaleStateException.class}) 
public void updateA() { 
    A a = findA(); 
    B b = new B(); 
    // Update "a" somehow 
    a.update(); 
    // "b" is saved on each retry! 
    save(b); 
} 

Jak podano w komentarzach, wydaje się, że transakcja nie jest rollbacked gdy StaleStateException występuje, więc instancja jest zapisywana przy każdym ponawianiu.

Czy można ponownie wykonać transakcję po ponowieniu próby?

Pożądanym zachowaniem jest zapisanie b tylko po pomyślnej aktualizacji a.

Odpowiedz

7

Myślę, że może to być coś związanego z konfiguracją @Retryable.

Ponieważ dokumentacja mówi: https://docs.spring.io/spring-batch/trunk/reference/html/retry.html#statelessRetry, ponowny zapis bezstanowy to nic innego jak cykl, który wywołuje tę samą metodę, aż do jej poprzedniego.

Problem polega na tym, że za każdym razem, gdy się nie uda, pierwszy wywoływany interceptor jest urządzeniem do ponownej próby, które nie spowoduje ponownego wyrzucenia wyjątku, więc nigdy nie osiągnie wartości @Transactional.

Co się dzieje, to że każda ponowna próba będzie następować po domyślnej propagacji transakcji, która ponownie użyje tej samej otwartej transakcji z new B() w kontekście.

Możesz sprawdzić, czy jestem na właściwej pozycji przez debugowanie: jeśli wprowadzisz drugą próbę i stwierdzisz, że A jest już zaktualizowany przed blokiem aktualizacji, to powinienem mieć rację.

można rozwiązać na 2 sposoby:

Albo podzielić na dwie przecznice (ponowić pierwszy z zagnieżdżonych transakcji)

@Retryable(value = {StaleStateException.class}) 
public void retryableUpdate() { 
    updateA(); 
} 

@Transactional(rollbackFor = {StaleStateException.class}) 
public void updateA() { 
    A a = findA(); 
    B b = new B(); 
    // Update "a" somehow 
    a.update(); 
    // "b" is saved on each retry! 
    save(b); 
} 

więc, że transakcja zostanie wycofana pierwszy.

Lub możesz postępować zgodnie z dokumentami i użyć stanowej ponownej próby.