Ответ
Уровень распространения (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 – это не просто так, они новые соединения к базе открывают. Так что если их бездумно юзать – можно так производительность ебнуть, что мало не покажется. Всё с умом, блядь, с расстановкой.