Understanding Spring Transaction Management: @EnableTransactionManagement, Transaction Propagation, and Synchronization
This article explains how Spring enables transaction management through @EnableTransactionManagement, details the underlying proxy mechanism, outlines the basic and detailed execution flow, describes transaction propagation types and their classifications, and shows how to force rollback and use TransactionSynchronization for custom callbacks.
Spring transaction management is essentially enabled by adding an @EnableTransactionManagement annotation, which registers two beans: AutoProxyRegistrar and ProxyTransactionManagementConfiguration . The former registers an InfrastructureAdvisorAutoProxyCreator bean that creates automatic proxies, while the latter defines three additional beans: BeanFactoryTransactionAttributeSourceAdvisor , AnnotationTransactionAttributeSource , and TransactionInterceptor .
The AnnotationTransactionAttributeSource checks whether a class or method is annotated with @Transactional . When such an annotation is present, the TransactionInterceptor intercepts the method call, creates a proxy, and routes the execution to its invoke() method.
1. @EnableTransactionManagement Working Principle
Enabling Spring transactions adds an Advisor to the container, which ultimately registers the two beans mentioned above.
2. Basic Execution Principle
During bean creation, InfrastructureAdvisorAutoProxyCreator checks if the bean matches BeanFactoryTransactionAttributeSourceAdvisor . If the bean or any of its methods has @Transactional , a dynamic proxy is created.
When the proxy method is invoked, it again checks for a matching advisor and then executes the TransactionInterceptor 's invoke() method, which follows these steps:
Use the configured PlatformTransactionManager to obtain a new database connection.
Set the connection's autocommit to false.
Execute the business method (SQL statements).
If no exception occurs, commit the transaction.
If an exception occurs, roll back the transaction.
3. Detailed Execution Flow
The article provides a flow diagram (link omitted) illustrating the above steps in detail.
4. Transaction Propagation Mechanism
When one method calls another, different propagation scenarios arise (e.g., both methods in the same transaction, separate transactions, or a mix). Spring handles these by checking the current thread's ThreadLocal for an existing connection; if one exists, it may suspend it, create a new connection, and later resume the original.
5. Propagation Categories
Examples of propagation types include REQUIRED (default), REQUIRES_NEW , etc. The article presents four case studies with code snippets demonstrating how each propagation behaves when exceptions are thrown.
@Component
public class UserService {
@Autowired
private UserService userService;
@Transactional
public void test() {
// sql in test method
userService.a();
}
@Transactional
public void a() {
// sql in a method
}
}Case 1 (REQUIRED) creates a single transaction for both methods; Case 2 rolls back both when an exception occurs; Case 3 also rolls back both; Case 4 uses Propagation.REQUIRES_NEW to create a separate transaction for the inner method, which rolls back independently before the outer transaction also rolls back.
6. Forced Rollback
If an exception is caught but you still want the transaction to roll back, call TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); inside the catch block.
@Transactional
public void test(){
try {
b();
} catch (Exception e) {
// build friendly error message
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
public void b() throws Exception {
throw new Exception();
}7. TransactionSynchronization
Spring allows registration of TransactionSynchronization callbacks to listen to transaction lifecycle events such as suspend, resume, beforeCommit, beforeCompletion, afterCommit, and afterCompletion.
@Transactional
public void test(){
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){
@Override public void suspend(){ System.out.println("test suspended"); }
@Override public void resume(){ System.out.println("test resumed"); }
@Override public void beforeCommit(boolean readOnly){ System.out.println("test before commit"); }
@Override public void beforeCompletion(){ System.out.println("test before completion"); }
@Override public void afterCommit(){ System.out.println("test after commit"); }
@Override public void afterCompletion(int status){ System.out.println("test after completion"); }
});
jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1')");
userService.a();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void a(){
// similar synchronization registration
jdbcTemplate.execute("insert into t1 values(2,2,2,2,'2')");
}The article concludes with a reminder that the content is for educational purposes and includes promotional messages unrelated to the technical content.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
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.