✅ When you call a @Transactional
method from another @Transactional
method in the same bean (or same Spring proxy):
- By default, Spring uses propagation
REQUIRED
, which means:- The inner
@Transactional
method joins the existing transaction of the outer method. - There’s still only one physical transaction and one session.
- Both methods share the same first-level cache/persistence context → batch fetching can work as long as the session stays open.
- The inner
@Transactional
public void outerMethod() {
List<Author> authors = em.createQuery("FROM Author", Author.class).getResultList();
for (Author author : authors) {
innerMethod(author); // transactional inside transactional
}
}
@Transactional
public void innerMethod(Author author) {
List<Book> books = author.getBooks(); // shares same transaction/session with outerMethod
...
}
✔ In this case, since both methods are called within the same proxy, and default propagation is REQUIRED
, innerMethod
runs inside the same transaction/session → batch fetching can work.
🚨 BUT there’s a catch:
Self-invocation → if you call this.innerMethod(...)
inside the same class, Spring’s proxy-based AOP will not apply the transactional behavior to the inner method, and your @Transactional
on innerMethod
will be ignored entirely!
✅ To make nested transactions effective, you need:
- Either the calls to go through a Spring proxy (e.g., call methods on another bean),
- Or use AOP-aware mechanisms like
ApplicationContext.getBean(ThisClass.class).innerMethod(...)
🟢 Summary of key behaviors:
1️⃣ Transactional inside transactional, same proxy, default propagation (REQUIRED) → inner joins outer transaction → same session → batching can work.
2️⃣ Transactional with different propagation (e.g., REQUIRES_NEW) → inner opens new transaction → new session → batching won’t work across them.
3️⃣ Transactional inside same class by self-call → ignored → inner @Transactional
won’t apply unless the call goes through Spring’s proxy.
🔥 Bottom line:
In Spring, nested
@Transactional
methods with propagationREQUIRED
share the same transaction/session, enabling batch fetching; but self-invocation can silently break transactional behavior.