Логотип Workflow

Article

Updated at:

Transactions

Этап 8 - Transactions: ACID, @Transactional, rollback и isolation

Transaction - это граница, внутри которой несколько изменений базы становятся одной завершенной операцией. Классический пример - банковский перевод. Если деньги списались со счета A, но не попали на счет B, данные повреждены. Transaction делает операцию atomic: оба изменения commit, либо оба изменения rollback.

Transaction boundary

ACID

ACID описывает четыре свойства надежных transactions. Atomicity означает, что все шаги считаются одной единицей. Consistency означает, что база переходит из одного корректного состояния в другое корректное состояние. Isolation означает, что concurrent transactions не мешают друг другу способом, нарушающим выбранные правила isolation. Durability означает, что committed changes сохраняются после сбоев.

Шаг переводаЧто ломается без transactionКакая идея ACID защищает
Списать деньги со счета ADebit сохранился, но credit на счет B упал.Atomicity делает так, что оба updates commit-ятся или rollback-ятся вместе.
Проверить, что balance не уходит в минусПравило обходится, и база хранит некорректное состояние.Consistency держит операцию внутри valid business и database rules.
Два перевода трогают один accountОдна операция читает промежуточное или устаревшее значение другой.Isolation управляет тем, что concurrent transactions могут видеть.
Commit успешен, а приложение падаетПользователь видит успех, но данные исчезают после restart.Durability означает, что committed changes сохранены.

@Transactional в Spring

В Spring applications @Transactional обычно ставят на service methods. Service method описывает полный use case, поэтому это естественная transaction boundary. Controller не должен владеть transaction, потому что он работает с HTTP. Repository не должен владеть всей business transaction, потому что один use case может вызвать несколько repositories.

@Transactional
public void transfer(Long fromId, Long toId, BigDecimal amount) {
    Account from = accountRepository.findById(fromId).orElseThrow();
    Account to = accountRepository.findById(toId).orElseThrow();
    from.withdraw(amount);
    to.deposit(amount);
}

Когда method начинается, Spring открывает transaction. Загруженные entities становятся managed. Когда поля меняются, Hibernate отслеживает изменения. Если method завершается нормально, Spring делает commit, а Hibernate flush SQL. Если выбрасывается runtime exception, Spring по умолчанию делает rollback.

Rollback

Automatic rollback обычно происходит для unchecked exceptions. Checked exceptions по умолчанию не запускают rollback, если это не настроено. Это важно, когда service methods выбрасывают business exceptions. Можно использовать rollbackFor, если default недостаточен, но не стоит настраивать rollback случайно. Exception model должна соответствовать domain operation.

Manual rollback существует, но редко является первым выбором. Понятные exceptions и declarative transactions проще читать и тестировать.

Isolation levels и propagation

Isolation управляет тем, как одна transaction видит изменения других transactions. READ_UNCOMMITTED - самый слабый уровень и допускает dirty reads в базах, которые это поддерживают. READ_COMMITTED предотвращает dirty reads и часто является default. REPEATABLE_READ сохраняет повторные чтения стабильными внутри transaction. SERIALIZABLE самый строгий и ближе всего к последовательному выполнению transactions, но может снижать concurrency.

Propagation управляет тем, что происходит, когда transactional method вызывает другой transactional method. REQUIRED присоединяется к существующей transaction или создает новую. Это обычный default. REQUIRES_NEW приостанавливает текущую transaction и начинает новую. SUPPORTS присоединяется к transaction, если она есть, но может работать и без нее.

Практика

Реализуй перевод денег между accounts. Загрузи оба accounts внутри одного @Transactional service method, уменьши один balance, увеличь другой и выбрось exception после первого изменения, чтобы показать rollback. Затем убери exception и проверь, что оба balances commit вместе. После этого посмотри SQL logs и убедись, что updates flush-ятся на commit.

Чек-лист понимания

  • Могу объяснить transaction boundary на примере банковского перевода.
  • Знаю четыре свойства ACID.
  • Понимаю, почему @Transactional ставят на service methods.
  • Могу объяснить default rollback behavior.
  • Знаю базовый смысл isolation и propagation.

Авторизуйтесь чтоб пройти тесты

Practice

Интерактивная практика

Выполните задания и сразу проверьте ответ.