private
Żadna metoda prywatna oznaczona adnotacją @Transactional nie będzie wykonana w ramach transakcji (chyba, że metoda ją wołająca już była w jakiejś). Na pewno adnotacja @Transactional nie zadziała na takiej metodzie.
try-catch
Jeśli metoda A oznaczona @Transactional wyłapuje wszystkie wyjątki rzucane przez metodę B (także oznaczoną @Transactional), to gdy metoda B rzuci wyjątek, dostaniemy UnexpectedRollbackException bez śladu oryginalnego wyjątku.
Dokładniejsze wyjaśnienie:
Załóżmy, że mamy repozytorium, które może czasami rzucić wyjątek (w naszym przypadku robi to zawsze). I metoda repozytorium jest oznaczona jak @Transactional:
@Repository
public class SomeRepository {
@Transactional
public void throwRuntimeException(){
throw new RuntimeException();
}
}
I mamy także serwis, który wywołuje metodę repozytorium i chce przechwytywać te wyjątki żeby później je jakoś obsługiwać:
@Service
public class SomeService {
@Autowired
private SomeRepository someRepository;
@Transactional
public void catchRuntimeException() {
try {
repository.throwRuntimeException();
} catch (RuntimeException e) {
System.out.println(e.getMessage());
}
}
}
Niestety takie łapanie wyjątków powoduje drobne problemy. Wyjątek zostanie rzeczywiście przechwycony w bloku try-catch. Ale z powodu propagacji transakcji (z serwisu na repozytorium) cała transakcja (rozpoczęta w serwisie) jest już oznaczona jako rollbackOnly (jest przeznaczona do cycofania), co spowoduje w rezultacie rzucenie wyjątku UnexpectedRollbackException w momencie gdy przebieg programu dotrze do momentu, w którym kończy się kod wykonywany w ramach transakcji (w naszym przypadku będzie to koniec metody SomeService.catchRuntimeException).
Spring w momencie uruchamiania metody repozytorium opakowuje to wywołanie swoimi klasami i jeśli metoda rzucająca wyjątek nie obsłuży wyjątku w ramach tej metody (tylko zostanie on przekazany do metody wyżej), to klasa opakowująca oznacza transakcję jako rollbackOnly.
Po więcej przykładów zapraszam TransactionalTest.java .