Common Spring @Transactional Pitfalls and How to Fix Them

This article examines frequent Spring transaction problems—including ineffective transactions, rollback anomalies, and issues when combining read‑write separation with @Transactional—explains their root causes, and provides practical solutions such as engine changes, bean configuration, proxy usage, rollback settings, and proper propagation levels.

macrozheng
macrozheng
macrozheng
Common Spring @Transactional Pitfalls and How to Fix Them

Preface

In the previous article we analyzed Spring transactions in detail; this article discusses common problems when using @Transactional in daily work and their solutions.

Transaction ineffective

Transaction rollback issues

Problems when combining read‑write separation with transactions

Transaction Ineffective

We usually investigate the problem from two aspects.

Database Layer

Check whether the storage engine supports transactions. MySQL uses InnoDB by default, which supports transactions, but if the table is altered to MyISAM, transactions become ineffective. alter table table_name engine=myisam; Solution: Change the storage engine back to InnoDB.

Business Code Layer

Ensure the bean is managed by Spring (e.g., annotated with @Service or @Component) and that @Transactional is placed on a public method.

Solution: Add @Service annotation to the bean.

Also, @Transactional must be on a public method; otherwise it is ignored.

Solution: Change the method visibility to public.

Self‑invocation also disables transaction interception because the call bypasses the proxy.

Solution: Inject the bean into itself and call through the proxy, or use AopContext.currentProxy().

@Service
public class DmzService {
    @Autowired
    DmzService dmzService;

    @Transactional
    public void save(A a, B b) {
        dmzService.saveB(b);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveB(B b) {
        dao.saveB(b);
    }
}

Alternatively, enable proxy exposure and call via AopContext:

@Service
public class DmzService {
    @Transactional
    public void save(A a, B b) {
        ((DmzService) AopContext.currentProxy()).saveB(b);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveB(B b) {
        dao.saveB(b);
    }
}
Enable proxy exposure in configuration: @EnableAspectJAutoProxy(exposeProxy = true)

Transaction Rollback Issues

Two typical situations:

Rollback is expected but the transaction commits.

Commit is expected but the transaction is marked rollback‑only.

The first case usually stems from misunderstanding the rollbackFor attribute. By default Spring rolls back only for unchecked exceptions ( RuntimeException) and Error.

public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}

Solution: Specify rollbackFor = Exception.class (or other checked exceptions) on the @Transactional annotation.

The second case occurs when an inner transaction marks the outer one as rollback‑only. Even if the outer method catches the exception, the transaction cannot be committed.

Solution: Adjust propagation ( REQUIRES_NEW or NESTED) or explicitly set rollback status only when appropriate.

@Transactional
public void testRollbackOnly() {
    try {
        indexService.a();
    } catch (ClassNotFoundException e) {
        TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
    }
}

Read‑Write Separation with Transactions

Read‑write separation can be implemented via multiple data sources or middleware such as MyCat.

If multiple data sources are used, a read‑write transaction must use the write node, while a read‑only transaction may use the read node.

When using middleware, any transaction typically forces all SQL to the write node.

Therefore, avoid annotating every service with @Transactional unless necessary, and be careful with the readOnly attribute, which may route reads to the master database and defeat the purpose of read‑write separation.

Summary

This article concludes the series on Spring transactions, summarizing common pitfalls and practical solutions to help developers avoid mistakes.

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.

transactionaoptransactionalrollback
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

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.