Будет ли откат транзакции при любых ошибках если не объявлен rollbackFor в Spring
Что проверяют
Знаете ли вы правило отката по умолчанию и, главное, условия, при которых оно вообще срабатывает. Следом обычно спрашивают, что будет, если исключение поймать внутри метода, и почему @Transactional не работает при вызове метода из того же класса.
Как отвечать
Нет, не при любых. Без rollbackFor Spring откатывает транзакцию только на непроверяемые исключения: наследники RuntimeException и Error. Проверяемое исключение пролетает наружу, а транзакция при этом фиксируется. Так сделано по аналогии с EJB: проверяемое исключение считается ожидаемым бизнес-исходом.
Настройка:
rollbackFor = Exception.class: откат на любое исключение;noRollbackFor: обратное, исключение, при котором транзакцию нужно зафиксировать;- глобально: в Spring Framework 6.2 появился
@EnableTransactionManagement(rollbackOn = RollbackOn.ALL_EXCEPTIONS), чтобы не писатьrollbackForна каждом методе.
Дальше покажите, что откат зависит не только от типа исключения:
- исключение должно выйти за пределы прокси: если вы его поймали в
catchи не пробросили, отката не будет. Вручную пометить транзакцию можно черезTransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); - вызов через прокси:
this.method()внутри того же бина идёт мимо прокси, аннотация не срабатывает; - обёртки Spring: при работе через
JdbcTemplateили JPASQLExceptionпревращается вDataAccessException, а этоRuntimeException, так что откат будет.
Где спотыкаются
Отвечают «да, на любую ошибку». Это классический баг: сервис бросает своё проверяемое BusinessException, часть данных уже записана, и она фиксируется.
Ловят во внешнем методе исключение, вылетевшее из внутреннего @Transactional с REQUIRED, и продолжают работу. Внутренний метод уже пометил общую транзакцию как rollback-only, и на коммите внешний получает UnexpectedRollbackException.
Пишут на Kotlin и забывают, что компилятор проверяемые исключения не различает, а Spring различает. IOException из Kotlin-кода по-прежнему не откатывает транзакцию без rollbackFor.