Why @Transactional Fails in Spring and How to Fix It
This article explains three common reasons why the @Transactional annotation may not work in Spring—non‑public methods, self‑invocation, and catching exceptions—provides code examples for each case, and details the underlying proxy and transaction management mechanisms to help developers resolve the issues.
Transactional Failure Scenarios
The @Transactional annotation can become ineffective in three typical situations.
1. Method Modifier Is Not Public
When a method annotated with @Transactional is declared with a non‑public access modifier, Spring's proxy mechanism ignores it, so the transaction is never started.
@Component
public class TestServiceImpl {
@Resource
TestMapper testMapper;
@Transactional
void insertTestWrongModifier() {
int re = testMapper.insert(new Test(10,20,30));
if (re > 0) {
throw new NeedToInterceptException("need intercept");
}
testMapper.insert(new Test(210,20,30));
}
}Calling this method from another bean results in the insert operation not being rolled back because the transaction never begins. Changing the method to public restores normal transaction behavior.
2. Internal Self‑Invocation
When a @Transactional method is called from another method within the same class, the call bypasses the Spring proxy (it uses this), so the transaction advice is not applied.
@Component
public class TestServiceImpl {
@Transactional
public void insertTestInnerInvoke() {
// transactional logic
}
public void testInnerInvoke() {
// self‑invocation – no proxy
insertTestInnerInvoke();
}
}To make the transaction work, the internal call must go through the proxy, e.g., by injecting the bean itself:
@Component
public class TestServiceImpl {
@Resource
TestServiceImpl self;
@Transactional
public void insertTestInnerInvoke() { /* ... */ }
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 cannot detect the failure and therefore will not roll back the transaction.
@Component
public class TestServiceImpl {
@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
}
}
}Because the exception never propagates out of the method, the transaction commit proceeds and no rollback occurs.
Underlying Mechanism
Spring creates a proxy for beans that contain @Transactional methods. During bean initialization, BeanFactoryTransactionAttributeSourceAdvisor scans for public methods with the annotation. The core check is performed in
AbstractFallbackTransactionAttributeSource#computeTransactionAttribute, which returns null for non‑public methods (the allowPublicMethodsOnly() guard).
The proxy intercepts method calls via CglibAopProxy.DynamicAdvisedInterceptor#intercept. It retrieves the applicable interceptors with advised.getInterceptorsAndDynamicInterceptionAdvice. If no @Transactional metadata is found for the invoked method, the call proceeds without transaction management.
When a transaction is active, TransactionAspectSupport#invokeWithinTransaction wraps the method execution. If an exception is thrown, the catch block triggers completeTransactionAfterThrowing to roll back. Swallowing the exception prevents this rollback.
Visual Aid
In summary, @Transactional may not work because the method is not public, the call bypasses the proxy via self‑invocation, or the method catches exceptions without re‑throwing them.
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.
