Получить доступ

@Transactional: propagation, isolation, rollback

Декларативные транзакции через прокси, ловушка self-invocation, уровни распространения, правила отката и readOnly/isolation.

Декларативные транзакции через прокси

@Transactional — это AOP поверх бина: Spring оборачивает его в прокси, который на входе в метод открывает транзакцию, при штатном возврате коммитит, а при исключении откатывает. Сам метод о транзакции «не знает» — управление декларативно.

Под капотом работает PlatformTransactionManager, а соединение/EntityManager привязываются к текущему потоку через TransactionSynchronizationManager (ThreadLocal). Отсюда важное следствие: при уходе в другой поток (@Async, реактивный код) транзакционный контекст теряется.

Ловушка self-invocation

Поскольку транзакцию открывает ПРОКСИ, она срабатывает только при вызове через него. Если @Transactional-метод вызывается изнутри того же класса через this, вызов идёт мимо прокси — и транзакции не будет. Это самая частая «загадочная» ошибка с транзакциями.

Обходы: вынести метод в отдельный бин (вызов через его прокси), внедрить самого себя через прокси, или перейти на AspectJ weaving. По той же причине @Transactional на private-методах не работает никогда. С Spring 6 при CGLIB-прокси аннотация действует и на protected и package-private методах, но надёжнее держать транзакционные методы публичными.

@Service
class BillingService {
    @Transactional public void charge() { /* ... */ }
    public void process() {
        charge(); // вызов через this — мимо прокси, транзакции НЕТ!
    }
}

Propagation

Propagation управляет поведением при вложенных транзакционных вызовах. REQUIRED (по умолчанию) — присоединиться к существующей или создать новую. REQUIRES_NEW — приостановить текущую и открыть независимую новую. NESTED — точка сохранения (savepoint) внутри текущей. Есть и SUPPORTS/MANDATORY/NEVER/NOT_SUPPORTED для тонких случаев.

Подводный камень REQUIRES_NEW: он берёт ВТОРОЕ соединение из пула, удерживая первое приостановленным — два занятых соединения на один запрос при нагрузке могут исчерпать пул и привести к взаимоблокировке. Используют его осознанно (например, для аудита, который должен сохраниться независимо от отката основной операции).

Правила отката

Ключевая ловушка собеседований: по умолчанию Spring откатывает транзакцию только на unchecked-исключениях (RuntimeException и Error). На checked-исключении транзакция по умолчанию КОММИТИТСЯ — чтобы откатывать, указывают rollbackFor (а noRollbackFor — наоборот).

Ещё нюанс: если внутренняя транзакция помечена «rollback-only», внешняя при попытке коммита получит UnexpectedRollbackException. И помните — commit/flush происходит уже на выходе из метода, поэтому нарушение constraint БД может «прилететь» исключением после тела.

@Transactional(rollbackFor = IOException.class) // иначе checked не откатит
void importFile() throws IOException { /* ... */ }

Isolation и readOnly

Атрибут isolation задаёт уровень изоляции (READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE) — его обеспечивает БД, а не Spring (см. урок про транзакции в разделе баз данных). timeout ограничивает длительность транзакции.

readOnly = true — это оптимизация: подсказка Hibernate не делать dirty checking и flush (нечего сохранять) и драйверу/БД — что запись не планируется. Для чисто читающих методов это снижает накладные расходы. readOnly не «запрещает запись» жёстко — это оптимизирующий флаг.

Как отвечать на собеседовании

Объясните @Transactional как прокси-AOP (открыть/коммит/откат вокруг метода, контекст в ThreadLocal). Обязательно — self-invocation (вызов через this мимо прокси → нет транзакции; и про private). Разберите propagation (REQUIRED vs REQUIRES_NEW + риск пула) и главную ловушку отката (checked не откатывается по умолчанию → rollbackFor). Добавьте readOnly как оптимизацию и потерю контекста при @Async.

Вопросы по теме

Читайте также

Закрытая бета

Знать тему — половина дела

На живом собеседовании sotto слышит вопрос интервьюера и показывает короткий ответ и план. Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем