Ответ
Нет, новая транзакция не создастся по умолчанию. Внутренний вызов будет выполняться в рамках существующей транзакции внешнего метода.
Почему? Это связано с работой Spring AOP (проксирование). Когда вызов происходит внутри одного класса, он минует прокси-обертку, и механизм управления транзакциями не перехватывается. Такое поведение называется self-invocation.
Пример:
@Service
public class TransactionalService {
@Transactional
public void outerMethod() {
// Начинается транзакция Tx1
innerMethod(); // Этот вызов НЕ приведет к созданию новой транзакции.
// Вся работа выполняется в Tx1
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void innerMethod() {
// Несмотря на аннотацию, этот код выполнится в контексте Tx1,
// а НЕ в новой независимой транзакции.
}
}
Как заставить это работать?
- Вынести
innerMethod()в отдельный бин и инжектить его. - Получить ссылку на собственный прокси через
AopContext.currentProxy()(требует активацииexposeProxy). - Использовать программируемое управление транзакциями (TransactionTemplate).
Ответ 18+ 🔞
Да ты посмотри на эту дичь, блядь! Вот сидишь, думаешь: «Аннотацию повесил — и вся недолга, теперь всё в транзакциях». А тут, сука, выходит, что не всё так просто, ёпта!
Вот представь, ты написал метод, обвешал его @Transactional, как ёлку игрушками. Внутри него зовёшь другой метод, тоже с транзакцией. И думаешь: «Ну сейчас он у меня в новой, независимой транзакции отработает, красота!» А него, блядь, ни хуя!
Почему? А потому что вся эта магия с транзакциями — это прокси-обёртки, которые Spring натягивает на твой бин. И когда ты извне вызываешь outerMethod(), то попадаешь как раз в эту обёртку. Она говорит: «О, транзакция!» — и заворачивает вызов в неё.
А вот когда внутри outerMethod() ты просто так, по-пацански, вызываешь innerMethod() — ты же вызываешь его напрямую, минуя всю эту прокси-хуярси! Ты как будто обходишь турникет в метро, блядь. Spring про этот вызов ничего не знает, и его аннотации — просто красивые бумажки. Вся работа идёт в той же самой, первой транзакции. Это называется self-invocation, или, по-нашему, «самоёбство».
Вот смотри, как это выглядит в коде, сука:
@Service
public class TransactionalService {
@Transactional
public void outerMethod() {
// Тут стартует транзакция Tx1, всё чинно-благородно.
innerMethod(); // А вот этот вызов — пиздец обман! Новой транзакции НЕ БУДЕТ!
// Всё варится в одной кастрюле Tx1.
}
@Transactional(propagation = Propagation.REQUIRES_NEW) // Смотри какая аннотация пафосная! НОВАЯ! А по факту — хуй соси.
public void innerMethod() {
// И этот код, вместо того чтобы работать в своей песочнице, будет тащиться в старой транзакции Tx1.
// Аннотация проигнорирована нахуй.
}
}
Вот так-то, блядь. Ожидание — отдельная независимая транзакция, реальность — всё в одной куче.
Ну и как с этим бороться, ебать? Варианты есть:
- Самый нормальный. Вынести этот внутренний метод в отдельный бин. И инжектить его себе. Тогда Spring будет управлять вызовами к нему через прокси, и транзакции заработают как надо. Чистая архитектура, мать её.
- Костыль, но работает. Заставить Spring показывать тебе твой же собственный прокси через
AopContext.currentProxy(). Но для этого надо включить какую-то настройку (exposeProxy), и выглядит это всё как подпольная сделка, честно говоря. - Для мазохистов. Забить на декларативные аннотации и управлять транзакциями вручную, через
TransactionTemplate. Сам всё запускаешь, сам завершаешь. Полный контроль, но и возни — овердохуища.
Короче, запомни, чувак: транзакции в Spring — это магия, но магия работает только через прокси. А если ты внутри класса сам себя вызываешь — это не магия, а самообман, блядь.