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.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Why @Transactional Fails in Spring and How to Fix It

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

Spring proxy illustration
Spring proxy illustration

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaaopspringSpring Boottransactionaldatabase transaction
Java Backend Technology
Written by

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!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.