Why @Transactional Sometimes Fails in Spring and How to Fix It
This article explains three common situations where Spring's @Transactional annotation becomes ineffective—non‑public methods, internal self‑calls, and swallowed exceptions—detailing the underlying proxy mechanism, code examples, test results, and practical workarounds to ensure proper transaction management.
Transactional Annotation Failure Scenarios
The @Transactional annotation can become ineffective in three typical cases: when the annotated method is not public , when a transactional method is invoked internally within the same class, and when the method catches exceptions without re‑throwing them.
1. Non‑public Method Modifier
Spring creates transaction proxies only for public methods. If a method is declared with default, protected, or private visibility, the proxy does not apply and the transaction never starts.
/**
* @author zhoujy
*/
@Component
public class TestServiceImpl {
@Resource
TestMapper testMapper;
@Transactional
void insertTestWrongModifier() { // not public
int re = testMapper.insert(new Test(10,20,30));
if (re > 0) {
throw new NeedToInterceptException("need intercept");
}
testMapper.insert(new Test(210,20,30));
}
}A test invoking this method shows that the testMapper.insert(new Test(10,20,30)) operation is not rolled back when an exception is thrown.
2. Internal Self‑Invocation
When a transactional method is called from another method of the same class, the call bypasses the proxy (it uses this), so the transaction logic is skipped.
public class TestServiceImpl implements TestService {
@Transactional
public void insertTestInnerInvoke() {
// transactional logic
}
public void testInnerInvoke() {
// internal call – no proxy
insertTestInnerInvoke();
}
}The corresponding test demonstrates that the database insert is not rolled back. A common workaround is to inject the bean into itself and call the method through the injected proxy:
@Resource
private TestServiceImpl self;
public void testInnerInvoke() {
self.insertTestInnerInvoke(); // goes through proxy
}3. Swallowing Exceptions
If a transactional method catches an exception and does not re‑throw it, Spring never sees the exception, so the transaction is committed instead of rolled back.
@Transactional
public void insertTestCatchException() {
try {
int re = testMapper.insert(new Test(10,20,30));
if (re > 0) {
throw new NeedToInterceptException("need intercept");
}
testMapper.insert(new Test(210,20,30));
} catch (Exception e) {
System.out.println("i catch exception"); // exception swallowed
}
}The test shows that even though NeedToInterceptException is thrown, the subsequent insert is not rolled back because the exception is handled inside the method.
Underlying Proxy Mechanics
Spring scans beans for @Transactional annotations using BeanFactoryTransactionAttributeSourceAdvisor. The canApply method iterates over all declared methods; if a method is non‑public,
AbstractFallbackTransactionAttributeSource#computeTransactionAttributereturns null, preventing proxy creation for that bean.
During method interception, CglibAopProxy.DynamicAdvisedInterceptor#intercept retrieves the interceptor chain via advised.getInterceptorsAndDynamicInterceptionAdvice. If the target method lacks a transactional attribute, the chain is empty and the original method executes without transaction management.
When a transaction is active, TransactionAspectSupport#invokeWithinTransaction opens the transaction, invokes the method, and rolls back in the catch block. Swallowed exceptions or bypassed proxies prevent this rollback logic from running.
Summary
In summary, @Transactional fails when the method is not public, when internal self‑calls bypass the proxy, or when exceptions are caught and not re‑thrown. Understanding Spring's AOP proxy creation and interception flow helps developers avoid these pitfalls and ensure reliable transaction management.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
