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

Ответ

Уровень распространения (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 создают отдельные физические соединения с БД, что может повлиять на производительность.

Ответ 18+ 🔞

А, слушай, вот эта ваша транзакционная магия в Спринге! Ну, Propagation, блядь. Это ж про то, как один метод с другим в одной песочнице играть будет, или свою, отдельную, гордую, блядь, песочницу нахуй заведёт.

Смотри, табличку эту, она вроде как умная, но я тебе на пальцах, сука, объясню, как оно живёт.

Вот REQUIRED – это как по умолчанию, стандарт-пакет, блядь. Ты заходишь в метод – о, транзакция уже есть? Отлично, работаем в ней. Нету? Ну, ёпта, щас создадим, не проблема. Как 90% всей хуйни и должно работать.

А вот REQUIRES_NEW – это, сука, максималист! Ему похуй на твои текущие разборки. Он всегда, блядь, свой собственный шатл запускает. Старую транзакцию – на паузу, щёлк! И пошёл своё делать. И вот это вот – для аудита, логирования, чтобы даже если вся основная операция накрылась медным тазом, запись в лог всё равно улетела и сохранилась. Независимый тип, блядь.

NESTED – хитрая жопа. Это как внутри большой транзакции создать маленькую, безопасную песочницу. Если в этой мелкой песочнице что-то пошло не так – её откатят, но большую-то транзакцию это не заденет! Красота, да? Только вот, сука, не все драйверы и менеджеры это поддерживают, имей в виду.

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(to, amount); // Положили
        logAudit(from, to, amount); // А вот это, сука, отдельная история!
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logAudit(Account from, Account to, BigDecimal amount) {
        // Вот эта запись в лог улетит в БД, даже если весь перевод выше наебнется и откатится.
        // Своя, независимая судьба, блядь!
    }
}

И да, запомни, чувак: REQUIRES_NEW и NESTED – это не просто так, они новые соединения к базе открывают. Так что если их бездумно юзать – можно так производительность ебнуть, что мало не покажется. Всё с умом, блядь, с расстановкой.