Mastering Spring @Transactional: Propagation, Rollback, and Proxy Pitfalls
This article explains Spring’s transaction propagation behaviors, rollback mechanisms, common pitfalls such as self‑invocation and non‑public methods, and demonstrates how to correctly use @Transactional with AOP proxies, including practical code examples and solutions for ensuring transaction consistency.
Introduction
During a recent work sprint I investigated Spring's @Transactional handling, ran several tests, and compiled the findings for developers who need reliable transaction control.
Transaction Propagation Behaviors
TransactionDefinition.PROPAGATION_REQUIRED: Join the existing transaction if present; otherwise create a new one (default). TransactionDefinition.PROPAGATION_REQUIRES_NEW: Always start a new transaction and suspend any existing one. TransactionDefinition.PROPAGATION_SUPPORTS: Join the existing transaction if present; otherwise execute non‑transactionally. TransactionDefinition.PROPAGATION_NOT_SUPPORTED: Execute non‑transactionally and suspend any existing transaction. TransactionDefinition.PROPAGATION_NEVER: Execute non‑transactionally; throw an exception if a transaction exists. TransactionDefinition.PROPAGATION_MANDATORY: Join the existing transaction; throw an exception if none exists. TransactionDefinition.PROPAGATION_NESTED: If a transaction exists, create a nested transaction; otherwise behaves like PROPAGATION_REQUIRED.
Rollback Mechanism
Spring’s AOP‑based declarative transaction management rolls back on unchecked (runtime) exceptions. The transaction starts before the business method and commits or rolls back after it, depending on whether a RuntimeException is thrown.
If a method catches an exception with try{ }catch(Exception e){ }, the caught block runs outside the transaction; to trigger a rollback you must re‑throw a RuntimeException from the catch block.
Common Pitfalls
Self‑invocation (calling another method of the same class via this) bypasses the proxy, so @Transactional on the called method is ineffective. Additionally, the annotated method must be public; non‑public methods are ignored by the proxy.
Solution Using Proxy
Obtain the current proxy with AopContext.currentProxy() and invoke the target method through it. Enable proxy exposure with @EnableAspectJAutoProxy(exposeProxy = true). The invoked method must be public to receive transaction advice.
Code Example
@RestController
public class TransactionalController {
@Autowired
private TransactionalService transactionalService;
@PostMapping("transactionalTest")
public void transacionalTest() {
transactionalService.transactionalMethod();
}
} public interface TransactionalService {
void transactionalMethod();
}Summary
Key takeaways: @Transactional ensures a method runs within a transaction, but only if the method is public and invoked through a Spring proxy; self‑invocation bypasses the proxy, rendering the annotation ineffective; use AopContext.currentProxy() or expose the proxy to call other transactional methods; to give each method its own transaction, invoke them via the proxy with appropriate propagation settings.
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.
