Ответ
Поведение по умолчанию:
- Откат транзакции для
RuntimeExceptionиError - Без отката для проверяемых исключений (
checked exceptions)
Настройка поведения:
// Откат для всех исключений
@Transactional(rollbackFor = Exception.class)
// Не откатывать для конкретных исключений
@Transactional(noRollbackFor = {NullPointerException.class, IllegalArgumentException.class})
// Настройка изоляции и распространения
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
Пример с потерей данных:
@Transactional
public void transferMoney(Account from, Account to, BigDecimal amount) {
from.withdraw(amount); // Успешно выполняется
// Здесь возникает RuntimeException
to.deposit(amount); // Не выполняется, но withdraw уже зафиксирован
}
Важные особенности:
-
Перехват исключений: Если исключение перехватывается внутри метода, транзакция не откатывается автоматически:
try { riskyOperation(); } catch (RuntimeException e) { // Транзакция НЕ откатится! log.error("Operation failed", e); } -
Явный откат:
@Transactional public void process() { try { businessLogic(); } catch (Exception e) { // Принудительный откат TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); throw e; } } -
Self-invocation: Методы, вызываемые внутри одного класса, не попадают под управление транзакций Spring (проблема self-invocation).