Why @Transactional Fails: 6 Common Pitfalls and How to Fix Them
This article explains the purpose of Spring's @Transactional annotation, details its configurable attributes, and enumerates six typical scenarios—such as non‑public methods, wrong propagation settings, self‑invocation, and unsupported database engines—that cause the annotation to become ineffective, providing code examples and solutions.
Yesterday a follower asked which scenarios cause the @Transactional annotation to fail, so this article shares essential knowledge about @Transactional.
Transaction Types
Spring provides two transaction management mechanisms: programmatic (code‑driven) and declarative (annotation‑driven).
Programmatic Transaction
Example of manual transaction control:
try {
// TODO something
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw new InvoiceApplyException("异常失败");
}Declarative Transaction
Based on AOP, it decouples business logic from transaction handling. It can be configured via XML (using TX or AOP) or simply by adding the @Transactional annotation.
@Transactional
@GetMapping("/test")
public String test() {
int insert = cityInfoDictMapper.insert(cityInfoDict);
return insert + "";
}@Transactional Overview
Where Can It Be Applied?
The annotation can be placed on interfaces, classes, and class methods.
On class : all public methods inherit the same transaction attributes.
On method : method‑level attributes override class‑level ones.
On interface : not recommended; when using CGLIB proxies it may cause the annotation to be ignored.
Key Attributes
Propagation
Propagation.REQUIRED: join existing transaction or create a new one. Propagation.SUPPORTS: join if a transaction exists, otherwise execute non‑transactionally. Propagation.MANDATORY: must join an existing transaction, otherwise throw an exception. Propagation.REQUIRES_NEW: always start a new transaction, suspending the current one. Propagation.NOT_SUPPORTED: execute non‑transactionally, suspending any existing transaction. Propagation.NEVER: execute non‑transactionally and throw an exception if a transaction exists. Propagation.NESTED: behaves like REQUIRED.
Isolation
Isolation.DEFAULT Isolation.READ_UNCOMMITTED Isolation.READ_COMMITTED Isolation.REPEATABLE_READ Isolation.SERIALIZABLETimeout
Specifies the transaction timeout in seconds; default is -1 (no timeout).
ReadOnly
Indicates whether the transaction is read‑only; default is false.
rollbackFor
Specifies exception types that should trigger a rollback.
noRollbackFor
Specifies exception types that should **not** trigger a rollback.
Common Scenarios Where @Transactional Fails
1. Applied to non‑public methods
If @Transactional is placed on a protected or private method, Spring will ignore it because the transaction interceptor only processes public methods.
2. Wrong propagation setting
Using propagation values such as SUPPORTS, NOT_SUPPORTED, or NEVER can cause the transaction not to roll back when an exception occurs.
3. Incorrect rollbackFor configuration
Only unchecked exceptions (RuntimeException, Error) trigger rollback by default. To roll back for checked exceptions, specify them with rollbackFor.
// Custom exception that should trigger rollback
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=MyException.class)
public void doSomething() { ... }4. Self‑invocation within the same class
When a method in the same class calls another @Transactional method, the call bypasses the Spring proxy, so the transaction is not applied.
@Transactional
public Integer insertB() throws Exception { ... }
private Integer A() throws Exception {
// Direct call – transaction will not be activated
this.insertB();
// ...
}5. Catching exceptions inside the transactional method
If a method catches an exception that should cause a rollback, the transaction will be committed, leading to data inconsistency.
@Transactional
public void A() throws Exception {
try {
// business logic that may throw
} catch (Exception e) {
e.printStackTrace(); // swallowing the exception disables rollback
}
}6. Database engine does not support transactions
MySQL's InnoDB engine supports transactions, while MyISAM does not; using a non‑transactional engine makes @Transactional ineffective.
Conclusion
@Transactional appears simple, but misunderstanding its usage can lead to many pitfalls. Hopefully this guide helps you avoid common mistakes.
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.
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.
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.
