When building enterprise-level applications with Spring Boot, transaction management is critical to ensure data integrity. Spring provides robust support for declarative transactions using the @Transactional
annotation. One of the most powerful aspects of this annotation is the propagation behavior.
In this post, we’ll cover:
- What is transaction propagation?
- Different propagation types in Spring
- Real-world use cases for each type
- Code examples
🔍 What is Transaction Propagation?
Transaction propagation determines how Spring should handle transaction boundaries when a method annotated with @Transactional
is called within the context of another transaction.
In simpler terms:
„If method A is already running in a transaction, and it calls method B (which is also transactional), what should happen to method B?“
🧭 Propagation Types
Spring defines the following propagation behaviors in the Propagation
enum:
Propagation Type | Description |
---|---|
REQUIRED |
Join current transaction or create a new one if none exists |
REQUIRES_NEW |
Always start a new transaction, suspending the existing one |
NESTED |
Execute within a nested transaction if a current one exists |
SUPPORTS |
Join the current transaction if available; else run non-transactionally |
NOT_SUPPORTED |
Always run non-transactionally, suspending any existing transaction |
NEVER |
Must run non-transactionally; throws exception if transaction exists |
MANDATORY |
Must join an existing transaction; throws exception if none exists |
✅ Common Use Cases with Examples
1. REQUIRED (default)
@Transactional(propagation = Propagation.REQUIRED)
public void processOrder() {
saveOrder();
updateInventory();
}
🧠 Use case: Most common. Use when all method calls should participate in the same transaction — either all succeed or all fail.
2. REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logAuditTrail() {
// Save audit log even if outer transaction fails
}
🧠 Use case:
Useful when you want to persist certain changes regardless of the outcome of the parent transaction.
🔄 Real-world example: Logging audit events, sending emails, or payment retries that should not roll back with business failure.
3. NESTED
@Transactional(propagation = Propagation.NESTED)
public void applyDiscount() {
// Rollback this logic only, not the parent transaction
}
🧠 Use case:
Allows partial rollbacks within the main transaction using savepoints.
⚠️ Note: Your database must support savepoints (e.g., H2, PostgreSQL, Oracle). MySQL with MyISAM doesn’t.
4. SUPPORTS
@Transactional(propagation = Propagation.SUPPORTS)
public List<Product> getAllProducts() {
return productRepository.findAll();
}
🧠 Use case:
Use when the method can work with or without a transaction (read-only operations).
5. NOT_SUPPORTED
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void sendEmailNotification() {
// Should not participate in any transaction
}
🧠 Use case:
Non-transactional operations (e.g., external API calls) that shouldn’t be part of the transaction to avoid rollback delays.
6. NEVER
@Transactional(propagation = Propagation.NEVER)
public void loadStaticData() {
// Throws exception if a transaction exists
}
🧠 Use case:
For methods that must not be called within a transaction, such as logging frameworks or async tasks.
7. MANDATORY
@Transactional(propagation = Propagation.MANDATORY)
public void updateUserPoints() {
// Only works if called from within a transaction
}
🧠 Use case:
To enforce that certain methods must be part of an active transaction — useful for modular business services.
⚠️ Common Pitfalls
- Calling transactional methods within the same class: Spring proxies won’t apply the transaction.
- 💡 Fix: Move the transactional method to a different class or use
AopContext
.
-
Confusing
REQUIRES_NEW
andNESTED
: They behave differently under rollback scenarios. -
REQUIRES_NEW misuse: Overuse can cause performance issues due to excessive transaction overhead.
🏁 Conclusion
Spring’s transaction propagation gives you fine-grained control over how transactions behave across service layers. Choosing the right propagation type depends on the business requirements, rollback strategies, and data integrity constraints.
📌 Quick Reference
Propagation | Joins Existing Txn? | Creates New Txn? | Throws if None Exists |
---|---|---|---|
REQUIRED | ✅ | ✅ (if none) | ❌ |
REQUIRES_NEW | ❌ (suspends) | ✅ | ❌ |
NESTED | ✅ (nested savepoint) | ✅ (if none) | ❌ |
SUPPORTS | ✅ | ❌ | ❌ |
NOT_SUPPORTED | ❌ (suspends) | ❌ | ❌ |
NEVER | ❌ | ❌ | ✅ |
MANDATORY | ✅ | ❌ | ✅ |