gotchajavaspringMajor
@Transactional does not work on private methods or same-class calls
Viewed 0 times
@Transactionalself-invocationproxy bypassAOPprivate method transactionCGLIB proxy
Problem
@Transactional is implemented via AOP proxy. If you call a @Transactional method from another method in the same class (self-invocation), or annotate a private method, the proxy is bypassed and no transaction is started.
Solution
Move the transactional logic to a separate Spring-managed bean, or inject a self-reference (less elegant):
// BAD — self-invocation bypasses proxy
@Service
public class OrderService {
public void processOrders() {
saveOrder(); // no transaction started
}
@Transactional
public void saveOrder() { ... }
}
// GOOD — separate bean
@Service
public class OrderService {
private final OrderWriter writer;
public void processOrders() {
writer.saveOrder(); // proxy is invoked
}
}
@Service
public class OrderWriter {
@Transactional
public void saveOrder() { ... }
}Why
Spring creates a CGLIB or JDK proxy around the bean. Calls from outside the bean go through the proxy and trigger advice (transactions). Internal calls use
this directly, skipping the proxy entirely.Gotchas
- Private methods annotated with @Transactional compile and run silently with no error — the annotation is simply ignored
- AspectJ compile-time weaving (not the default) can make @Transactional work on private methods and self-calls
- @Transactional on an interface method only works with JDK proxy (interface-based proxy), not CGLIB
Revisions (0)
No revisions yet.