Common Scenarios Where Spring @Transactional Fails and How to Avoid Them
This article explains the fundamentals of database transactions, introduces Spring's @Transactional annotation, and enumerates nine typical situations—such as incorrect method visibility, final methods, self‑invocation, unmanaged beans, multithreading, wrong propagation, swallowed exceptions, unsupported exception types, and non‑transactional storage engines—where transactions may not work as expected, offering guidance to prevent these pitfalls.
Database transactions are a logical unit of work that must either complete fully or not at all, adhering to the ACID properties (Atomicity, Consistency, Isolation, Durability).
In Spring projects, transaction management is usually achieved with the @Transactional annotation, which creates a proxy database connection that is shared across all operations within the transaction.
Typical usage in a service layer may involve multiple DAO calls, for example:
Controller layer:
UserService:addUser();
Service layer (UserService):
addUser(): insertUser() + insertLog()
Dao layer:
UserDao: insertUser();
LogDao: insertDao();Spring provides several attributes for @Transactional , such as isolation level, timeout, read‑only flag, rollback rules, and propagation behavior.
Isolation Levels
Isolation Level
Description
ISOLATION_DEFAULTUses the database's default isolation (MySQL: REPEATABLE READ).
ISOLATION_READ_UNCOMMITTEDAllows dirty reads; high concurrency but risks data inconsistency.
ISOLATION_READ_COMMITTEDPrevents dirty reads; a transaction sees only committed data.
ISOLATION_REPEATABLE_READPrevents non‑repeatable reads; the default for MySQL InnoDB.
ISOLATION_SERIALIZABLEHighest isolation; forces sequential execution of all SQL statements.
Other attributes include:
Timeout (e.g., @Transactional(timeout = 30) ) defines how long a transaction may run before being rolled back.
Read‑only (e.g., @Transactional(readOnly = true) ) marks a transaction as read‑only.
Rollback rules (e.g., @Transactional(rollbackFor = Exception.class) ) specify which exceptions trigger a rollback.
Propagation (e.g., @Transactional(propagation = Propagation.REQUIRED) ) defines how transactions interact; see the table below.
Propagation Types
Propagation
Description
PROPAGATION_REQUIRED
Join existing transaction or create a new one if none exists (default).
PROPAGATION_SUPPORTS
Execute within a transaction if present; otherwise run non‑transactionally.
PROPAGATION_MANDATORY
Must run within an existing transaction; throws exception otherwise.
PROPAGATION_REQUIRES_NEW
Always creates a new transaction, suspending any existing one.
PROPAGATION_NOT_SUPPORTED
Suspends any existing transaction and runs non‑transactionally.
PROPAGATION_NEVER
Throws an exception if a transaction exists; otherwise runs non‑transactionally.
PROPAGATION_NESTED
Executes within a nested transaction if a current transaction exists; otherwise behaves like REQUIRED.
Common Situations Where @Transactional Does Not Take Effect
Method Visibility : Only public methods are proxied. Example of a private method annotated with @Transactional will not start a transaction.
Final Methods : final methods cannot be overridden by CGLIB proxies, so transaction logic is not applied.
Self‑Invocation : Calling a transactional method from within the same class bypasses the proxy, preventing transaction creation.
Bean Not Managed by Spring : Classes not annotated with @Service , @Component , etc., are not registered as beans, so AOP cannot apply.
Multithreading : Transactions are bound to a single database connection; spawning new threads obtains separate connections, breaking the transaction.
Incorrect Propagation Settings : Using Propagation.NEVER on a method called within an existing transaction causes an exception.
Swallowing Exceptions : If a transactional method catches exceptions without rethrowing, Spring assumes normal execution and will not roll back.
Unhandled Exception Types : By default, only RuntimeException triggers rollback. Checked exceptions must be specified via rollbackFor .
Non‑Transactional Storage Engine : Using a database engine that does not support transactions (e.g., MySQL MyISAM) renders transaction annotations ineffective.
Conclusion
The article outlines the principles of Spring's @Transactional annotation and lists the most common reasons why a transaction may not be applied, such as self‑invocation, swallowed exceptions, and mismatched exception types. Understanding these pitfalls helps developers use @Transactional correctly and avoid data inconsistencies.
政采云技术
ZCY Technology Team (Zero), based in Hangzhou, is a growth-oriented team passionate about technology and craftsmanship. With around 500 members, we are building comprehensive engineering, project management, and talent development systems. We are committed to innovation and creating a cloud service ecosystem for government and enterprise procurement. We look forward to your joining us.
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.