Zum Inhalt springen

🌿 Understanding Transaction Propagation in Spring Boot

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

  1. 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.
  1. Confusing REQUIRES_NEW and NESTED: They behave differently under rollback scenarios.

  2. 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

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert