97. Транзакции: сколько будет при вызове b() и как сделать a() транзакционным #
Условие задачи:
📌 Дан код:
@Service
class SomeService {
@Transactional(propagation = Required_New)
public void a() {}
@Transactional
public void b() {
a();
}
}
SomeService.b();
Нужно ответить:
Сколько будет транзакций при вызове
b()?Почему?
Что сделать, чтобы второй вызов (
a()) тоже был транзакционным (REQUIRES_NEW)?
Спойлеры к решению
Подсказки
@Transactional работает через Spring AOP proxy.💡 Вызов
a() внутри b() — это self-invocation (внутренний вызов внутри одного объекта), он проходит мимо proxy.💡 Поэтому
REQUIRES_NEW не применится, если a() вызывается напрямую как a().💡 Нужно вызывать
a() через прокси: вынести a() в другой бин или получить self-proxy и вызвать через него.Решение
1) Сколько транзакций будет?
Будет 1 транзакция — та, что открывается на методе b().
2) Почему?
Spring применяет
@Transactionalчерез прокси (AOP).Вызов
SomeService.b()извне проходит через прокси → транзакция создаётся.Но внутри
b()вызовa()происходит как обычный вызов метода текущего объекта (this.a()фактически).Такой вызов не проходит через прокси, поэтому аннотация на
a()не срабатывает, иREQUIRES_NEWне создаёт новую транзакцию.
3) Как сделать, чтобы a() тоже был транзакционным (REQUIRES_NEW)?
Способ 1 (правильный и самый частый): вынести a() в отдельный сервис.
@Service
class AService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void a() {}
}
@Service
@RequiredArgsConstructor
class SomeService {
private final AService aService;
@Transactional
public void b() {
aService.a(); // вызов через proxy другого бина → REQUIRES_NEW сработает
}
}
Способ 2: вызвать через self-proxy (менее предпочтительно).
@Service
class SomeService {
@Autowired
private SomeService self; // прокси самого себя
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void a() {}
@Transactional
public void b() {
self.a(); // вызов через proxy → создаст новую транзакцию
}
}
Важно: должен быть включён proxy-mode и инъекция будет именно прокси-объекта.