Ответ
Нет, транзакция не начнется. Это классическое ограничение работы Spring AOP-прокси.
Причина:
Транзакционность в Spring реализуется через прокси-объекты. Внутренний вызов метода (this.transactionalMethod()) минует прокси, и, следовательно, перехватчик транзакций не срабатывает.
Пример проблемы:
@Service
public class UserService {
public void createUserNonTransactional(String name) {
// Этот вызов происходит напрямую, минуя прокси.
createUserTransactional(name); // Транзакция НЕ БУДЕТ создана!
}
@Transactional
public void createUserTransactional(String name) {
// Логика, требующая транзакции
userRepository.save(new User(name));
}
}
Способы решения:
-
Self-injection: Внедрить самого себя и вызывать метод через прокси.
@Service public class UserService { @Autowired private UserService self; // Ссылка на прокси public void createUserNonTransactional(String name) { self.createUserTransactional(name); // Теперь транзакция работает. } // ... } - Разделение на разные бины: Вынести транзакционный метод в отдельный сервисный компонент.
- Использование AspectJ mode: Настройка Spring на использование AspectJ для компоновки во время загрузки (LTW) или компиляции (CTW) обходит это ограничение, но сложнее в настройке.
Ответ 18+ 🔞
А, ну это же классика, блядь! Вечный геморрой с этими проксями в Spring'е. Сидишь такой, пишешь код, думаешь: «Ну всё, сейчас я тут транзакцию красиво заверну, и будет нам счастье». А в итоге нихуя не работает, и ты полдня ломаешь голову, как будто в первый раз.
Вот смотри, в чём тут собака зарыта, а точнее — сука, прокси минует. Spring, он же хитрая жопа, чтобы транзакции подсунуть, оборачивает твой бин в прокси-обёртку. И когда ты извне вызываешь метод, всё ок — попадаешь в прокси, он там транзакцию стартует, потом в реальный метод проваливается.
Но! Если ты внутри одного и того же бина из одного метода вызываешь другой — ты ж вызываешь его напрямую, через this. А this — это же не прокси, ёпта! Это просто объект, голый и неприкрытый. Прокси остаётся в сторонке, грустно курит, и транзакция, соответственно, не начинается. Вообще.
Вот тебе наглядный пиздец:
@Service
public class UserService {
public void createUserNonTransactional(String name) {
// А вот тут облом, чувак. Вызов идёт мимо всех этих spring'овых приблуд.
createUserTransactional(name); // Транзакция? НИ ХУЯ!
}
@Transactional
public void createUserTransactional(String name) {
// Тут по логике должна быть транзакция, но её нет, как нет и твоих надежд.
userRepository.save(new User(name));
}
}
И что делать-то, спросишь? Варианты есть, не печалься.
-
Самовнедрение (Self-injection). Звучит как извращение, но работает. Просишь Spring: «Дай-ка мне ссылку не на самого себя, а на свою же прокси-обёртку».
@Service public class UserService { @Autowired private UserService self; // Это не я, это моя официальная представительница — прокся! public void createUserNonTransactional(String name) { self.createUserTransactional(name); // А вот теперь — да, транзакция, мать её, работает! } // ... }Немного пахнет псиной, но жить можно.
-
Разделяй и властвуй. Выносишь весь транзакционный функционал в отдельный сервисный бин. И вызываешь уже его. Чистая архитектура, никакой магии.
-
AspectJ mode. Это уже для мазохистов и гиков. Настраиваешь Spring на использование полной мощи AspectJ, который будет впиливать транзакционность прямо в байт-код, минуя прокси. Работает везде, но настройка — тот ещё квест. Овердохуища мороки.
Короче, запомни раз и навсегда: внутренние вызовы в Spring AOP не работают. Это не баг, это такая пиздатая особенность. Теперь ты в курсе, и не будешь, как я когда-то, биться головой об клавиатуру, крича «Почему не начинается, блядь?!». Всё из-за этой прокси, сука.