Что такое Propagation (распространение транзакций) в Spring?

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

Ответ

Propagation (распространение транзакций) — это атрибут транзакции в Spring, определяющий, как должна вести себя транзакция, когда метод, помеченный @Transactional, вызывается из другого транзакционного метода.

Типы распространения (Propagation):

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

Примеры:

@Service
public class OrderService {
    @Transactional(propagation = Propagation.REQUIRED)
    public void placeOrder(Order order) {
        // Основная логика заказа в транзакции
        orderRepository.save(order);
        inventoryService.updateStock(order); // Вызов метода с REQUIRES_NEW
        // Если updateStock выбросит исключение, order НЕ будет откачен,
        // но stock обновится (т.к. его транзакция завершилась).
    }
}

@Service
public class InventoryService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateStock(Order order) {
        // Эта операция выполняется в своей собственной транзакции.
        // Её коммит или откат не влияет на транзакцию placeOrder.
        inventoryRepository.decrementStock(order.getItemId(), order.getQuantity());
    }
}

Важно: Поведение NESTED зависит от поддержки JDBC-драйвером точек сохранения (savepoints).