Understanding Spring @Transactional: Propagation Behaviors, Rollback Rules, and Proxy‑Based Usage
This article explains Spring's @Transactional annotation, detailing its propagation options, rollback mechanisms, common pitfalls such as self‑invocation and non‑public methods, and demonstrates how to correctly invoke transactional methods via proxy objects with practical code examples.
The author introduces Spring transaction management, focusing on the @Transactional annotation and its role in controlling transaction boundaries within Java backend applications.
Spring defines several propagation behaviors in TransactionDefinition: PROPAGATION_REQUIRED (join existing or create new), PROPAGATION_REQUIRES_NEW (always start a new transaction and suspend the current one), PROPAGATION_SUPPORTS (join if present, otherwise non‑transactional), PROPAGATION_NOT_SUPPORTED (run non‑transactionally and suspend any existing transaction), PROPAGATION_NEVER (throw an exception if a transaction exists), PROPAGATION_MANDATORY (must join an existing transaction), and PROPAGATION_NESTED (create a nested transaction when a transaction already exists, otherwise behaves like REQUIRED).
The rollback mechanism is also described: by default Spring rolls back on unchecked (runtime) exceptions, and the transaction boundary starts before the business method execution and commits or rolls back after the method returns, depending on whether a runtime exception was thrown.
Common pitfalls are highlighted: (1) a method annotated with @Transactional will not apply its transaction semantics when called from another method of the same class via this (self‑invocation), because the call bypasses the proxy; (2) the annotated method must be public, otherwise the proxy cannot intercept it; (3) catching exceptions inside a transactional method and not re‑throwing a runtime exception will prevent rollback.
To overcome self‑invocation, the article recommends obtaining the current proxy via AopContext.currentProxy() and invoking the target method on that proxy. This requires enabling proxy exposure with @EnableAspectJAutoProxy(exposeProxy = true) on the Spring Boot application class.
Code examples illustrate the setup:
@RestController
public class TransactionalController {
@Autowired
private TransactionalService transactionalService;
@PostMapping("transactionalTest")
public void transacionalTest() {
transactionalService.transactionalMethod();
}
}Service interface:
public interface TransactionalService {
void transactionalMethod();
}The article further explains how to ensure that multiple internal methods each run in their own transaction by invoking them through the proxy and using propagation settings such as REQUIRES_NEW. Diagrams (omitted here) illustrate the proxy‑based AOP flow.
In conclusion, the author summarizes three key points: (1) @Transactional ensures each method runs within a transaction, and any caught exception must be re‑thrown as a runtime exception for rollback; (2) methods must be public for the annotation to take effect; (3) self‑invocation bypasses proxy enhancement, so transactional behavior is lost unless the proxy is used.
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.
Architect's Tech Stack
Java backend, microservices, distributed systems, containerized programming, and more.
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.
