Home/Learn/Hibernate & JPA/Transaction Propagation

Transaction Propagation

Advanced
Transactions

REQUIRED (default, join or create), REQUIRES_NEW (always new, suspends outer), NESTED, SUPPORTS, NOT_SUPPORTED, MANDATORY, and NEVER — choose based on desired transactional boundary.

Overview

Transaction propagation controls what happens when a `@Transactional` method is called from within an existing transaction. Spring provides seven propagation types. **REQUIRED** (default): join the existing transaction or create a new one — both methods share the same commit/rollback boundary. **REQUIRES_NEW**: always start a fresh transaction, suspending the caller's — useful for audit logging that must commit even if the outer transaction rolls back. **NESTED**: use a savepoint within the outer transaction — rollback only affects the nested portion. **SUPPORTS**: join if there is a transaction, otherwise run non-transactionally. **NOT_SUPPORTED**: suspend any existing transaction and run non-transactionally. **MANDATORY**: must join an existing transaction; throws if none. **NEVER**: must not run in a transaction; throws if one exists.

REQUIRED and REQUIRES_NEW

**REQUIRED** is the right default for most service methods — they participate in whatever transaction the caller has. **REQUIRES_NEW** is the exception for operations that must succeed independently, like audit logging: if the business operation rolls back, the audit entry should still be persisted.

Spring — REQUIRED vs REQUIRES_NEW propagation
@Service
class OrderService {

    @Transactional                          // REQUIRED (default)
    public void placeOrder(Order order) {
        orderRepo.save(order);
        inventoryService.reserve(order);    // joins THIS transaction
        auditService.log("ORDER_PLACED");   // also joins — but we want it independent!
        // If inventoryService throws, audit log is also rolled back
    }
}

@Service
class AuditService {

    // REQUIRES_NEW: suspend caller's transaction, open a fresh one
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void log(String event) {
        auditRepo.save(new AuditEntry(event, LocalDateTime.now()));
        // This commits independently — even if placeOrder() rolls back
    }
}

// Important caveat: REQUIRES_NEW does NOT work when called on the same bean
// (Spring AOP proxy is bypassed for self-invocation)
// Always inject the service as a dependency, not via this.auditService.log()

NESTED, SUPPORTS, and MANDATORY

**NESTED** uses a JDBC savepoint: if the nested method throws, only changes since the savepoint are rolled back — the outer transaction can catch the exception and continue. **SUPPORTS** is useful for read methods that optionally benefit from a transaction context. **MANDATORY** is a safety assertion — call it only from within a transaction; throw otherwise.

Spring — NESTED and MANDATORY propagation
// NESTED — partial rollback via savepoint (JPA/InnoDB only)
@Transactional(propagation = Propagation.NESTED)
public void sendNotification(Order order) {
    notificationRepo.save(new Notification(order));
    // If this fails, outer order save is NOT rolled back (just notification attempt)
}

@Transactional
public void placeOrder(Order order) {
    orderRepo.save(order);
    try {
        notificationService.sendNotification(order);  // NESTED
    } catch (Exception e) {
        log.warn("Notification failed, order still placed");
    }
    // order is committed even if notification rolls back
}

// MANDATORY — must be called within an existing transaction
@Transactional(propagation = Propagation.MANDATORY)
public void updateInventory(String sku, int delta) {
    // Throws IllegalTransactionStateException if no active transaction
    // Use as a safety check: this method MUST run in a transaction
    inventoryRepo.adjustStock(sku, delta);
}

The Self-Invocation Trap

Spring's `@Transactional` is implemented via AOP proxies. When a method calls **another method on the same bean**, it bypasses the proxy and the called method's `@Transactional` annotation is completely ignored. This is the most common `@Transactional` bug. Fix: inject the bean via constructor (Spring creates the proxy), use `ApplicationContext.getBean()`, or restructure the code into separate beans.

Spring — self-invocation trap and fixes
@Service
class PaymentService {

    // BUG: self-invocation bypasses the proxy
    public void processPayment(Payment p) {
        this.charge(p);             // calls directly — @Transactional on charge() IGNORED
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void charge(Payment p) {
        // This NEVER runs in its own transaction when called via self-invocation
        paymentRepo.save(p);
    }
}

// FIX 1: inject self via constructor (Spring proxy)
@Service
class PaymentService {
    private final PaymentService self;   // injected proxy

    public PaymentService(PaymentService self) { this.self = self; }

    public void processPayment(Payment p) {
        self.charge(p);   // goes through proxy → @Transactional respected
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void charge(Payment p) { paymentRepo.save(p); }
}

// FIX 2 (better): extract charge() into a separate @Service bean

Key Points to Remember

  • 1REQUIRED (default): join existing or create new — both methods share the same transaction
  • 2REQUIRES_NEW: always creates a new transaction, suspending the outer — good for audit logs
  • 3NESTED: uses a JDBC savepoint — inner rollback does not affect the outer transaction
  • 4MANDATORY: throws if no active transaction exists — useful as a safety assertion
  • 5Self-invocation bypasses the AOP proxy — @Transactional on the called method is ignored
  • 6Fix self-invocation: inject the bean's proxy via constructor or extract to a separate bean

Interview Questions

Sign in to ask Aria
1

What is the difference between REQUIRED and REQUIRES_NEW propagation?

MediumOracle
2

What is the self-invocation problem with @Transactional and how do you fix it?

HardThoughtWorks
3

When would you use NESTED propagation over REQUIRES_NEW?

HardGoldman Sachs
4

What does MANDATORY propagation do and when is it useful?

MediumAccenture
5

If method A (@Transactional REQUIRED) calls method B (@Transactional REQUIRES_NEW) and B throws, does A roll back?

HardAmazon

Ask Aria about Transaction Propagation

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…