Можно ли в @Transactional указать исключения, при которых не происходит rollback
Что проверяют
Знаете ли вы правила отката по умолчанию и понимаете ли, что решение принимает прокси Spring, а не база. Ответ «да, есть noRollbackFor» это только начало: дальше спрашивают, что будет, если исключение выбросил вложенный метод.
Как отвечать
Да, можно. У @Transactional для этого четыре параметра: rollbackFor, rollbackForClassName, noRollbackFor, noRollbackForClassName.
Поведение по умолчанию назовите точно: откат идёт на RuntimeException и Error, а проверяемые исключения транзакцию не откатывают, она коммитится. Это правило Spring, а не JDBC, и оно регулярно удивляет тех, кто ждёт отката на IOException.
Детали, которые и проверяют:
- выбор правила: из нескольких подходящих побеждает ближайшее по иерархии наследования, поэтому
rollbackFor = Exception.classвместе сnoRollbackFor = BusinessException.classработает предсказуемо; - пометка rollback-only заразна: вложенный метод с
REQUIREDидёт в той же физической транзакции, и если его исключение вызвало откат, внешний на коммите получитUnexpectedRollbackException, даже когда поймал исключение и указалnoRollbackFor. Нужна изоляция, беритеREQUIRES_NEW; - прокси и self-invocation: вызов транзакционного метода изнутри того же класса идёт мимо прокси, аннотация не работает вовсе. То же с
privateиfinalметодами при CGLIB; - JPA после исключения:
EntityManager, получившийPersistenceException, непригоден для дальнейшей работы, коммитить в нём опасно. Практика:noRollbackForуместен для своих бизнес-исключений, а не для ошибок персистентности.
Упомяните и ручной аналог: TransactionAspectSupport.currentTransactionStatus().setRollbackOnly().
Где спотыкаются
Ловят исключение внутри метода и считают, что откат отменён. Если rollback-only уже поставлен вложенным вызовом, тишина в логах закончится UnexpectedRollbackException на коммите.
Ставят noRollbackFor = Exception.class, чтобы «ничего не падало». В базе остаётся половина операции, и разбираться будут через неделю по кривым данным.
Считают, что проверяемое исключение откатывает транзакцию. Без явного rollbackFor изменения зафиксируются, и метод отработает как успешный.