Какие уровни распространения транзакций (Propagation) существуют в Spring?

«Какие уровни распространения транзакций (Propagation) существуют в Spring?» — вопрос из категории Spring, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Уровень распространения (Propagation) в @Transactional определяет, как метод должен вести себя по отношению к существующей транзакции.

Уровень Описание Когда использовать
REQUIRED (По умолчанию) Использует текущую транзакцию, если она существует. Если нет — создаёт новую. Стандартный сценарий для большинства операций.
REQUIRES_NEW Всегда создаёт новую транзакцию. Приостанавливает текущую, если она существует. Для операций, которые должны быть committed независимо от результата внешней транзакции (например, логирование аудита).
NESTED Создаёт вложенную транзакцию внутри текущей. Откат вложенной не приводит к откату внешней (если она не откатится сама). Требует поддержки JDBC 3.0+ и DataSourceTransactionManager. Для логически выделенных подопераций, где нужен частичный откат.
SUPPORTS Выполняется в контексте существующей транзакции. Если транзакции нет — работает без неё. Для операций только чтения, которые могут выполняться как в транзакции, так и без.
MANDATORY Требует активной транзакции. Выбрасывает исключение, если транзакции нет. Когда метод должен вызываться только в рамках управляемой транзакции.
NOT_SUPPORTED Выполняется без транзакции. Приостанавливает текущую транзакцию, если она есть. Для операций, несовместимых с транзакциями (например, вызов внешнего API).
NEVER Запрещает выполнение в транзакции. Выбрасывает исключение, если транзакция активна. Для методов, которые не должны выполняться в транзакционном контексте.

Практический пример:

@Service
public class BankingService {

    @Transactional(propagation = Propagation.REQUIRED)
    public void transferMoney(Account from, Account to, BigDecimal amount) {
        withdraw(from, amount);
        // Даже если deposit() выполнится в новой транзакции,
        // откат здесь откатит и withdraw()
        deposit(to, amount);
        logAudit(from, to, amount); // REQUIRES_NEW — залогируется даже при откате перевода
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logAudit(Account from, Account to, BigDecimal amount) {
        // Логирование в отдельной транзакции
    }
}

Важно: REQUIRES_NEW и NESTED создают отдельные физические соединения с БД, что может повлиять на производительность.