Task Livecoding Java Transactions Self Invocation

97. Транзакции: сколько будет при вызове b() и как сделать a() транзакционным #

Условие задачи:
📌 Дан код:

@Service
class SomeService {

    @Transactional(propagation = Required_New)
    public void a() {}

    @Transactional
    public void b() {
        a();
    }
}

SomeService.b();

Нужно ответить:

  1. Сколько будет транзакций при вызове b()?

  2. Почему?

  3. Что сделать, чтобы второй вызов (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 и инъекция будет именно прокси-объекта.