Skip to main content

10.5 Propagation

When transactional methods call one another on the same thread, propagation determines how their transaction scopes interact.

Propagation can be specified through all three transaction APIs:

Annotation-Based Transactions
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void writeAuditLog(long orderId) {
...
}
Transaction Templates
txTemplate.execute(tranStatus -> {
...
return null;
}, Propagation.NESTED);
Programmatic Transactions
TransactionStatus tran = txManager.begin(Propagation.REQUIRED);

How to Choose​

Desired EffectRecommended Propagation
Most business methods: join existing or begin newREQUIRED
Audit and operation logs: commit independentlyREQUIRES_NEW
Allow one step to fail while the outer transaction continuesNESTED
Join existing but also allow non-transactional executionSUPPORTS
Run a block outside the current transactionNOT_SUPPORTED
Forbid transactional executionNEVER
Require an outer transactionMANDATORY

Join Existing Transaction (REQUIRED)​

Join an existing transaction or begin a new one if none exists.

  • Constant Propagation.REQUIRED
Order Creation Uses REQUIRED by Default
@Transactional
public void createOrder(long orderId) {
orderMapper.insertOrder(orderId);
orderMapper.insertOrderItems(orderId);
}

REQUIRED is the default: it begins a transaction if none exists or joins the outer transaction. Normal return from an inner method does not commit immediately; the outermost scope decides the outcome. In dbVisitor's local manager, inner REQUIRED rollback does not automatically mark the outer scope rollback-only. If the outer scope catches and suppresses the exception, these writes may still commit. To fail the entire operation, propagate the exception or explicitly mark the outer scope for rollback.

TimeTransaction ATransaction BEffect
T1beginBegin Transaction A
T2insert data1
T3beginJoin transaction A (no database action)
T4insert data2
T5commit/rollbackNo database action (outer scope decides)
T6insert data3
T7commit/rollbackCommit/rollback Transaction A

Independent Transaction (REQUIRES_NEW)​

Suspend any existing transaction and begin a new, independent transaction.

  • Constant Propagation.REQUIRES_NEW
Audit Logs Can Commit Despite Outer Failure
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void writeAuditLog(long orderId, String action) throws java.sql.SQLException {
jdbcTemplate.executeUpdate(
"insert into order_audit(order_id, action) values(?, ?)",
new Object[] { orderId, action }
);
}
info
  • Suspension temporarily makes the thread-bound Connection unavailable.
  • The manager then creates a new Connection for the current thread.
TimeTransaction ATransaction BEffect
T1beginBegin Transaction A
T2insert data1
T3beginSuspend A → new Connection B → begin B
T4insert data2
T5commit/rollbackCommit/rollback B → resume A
T6insert data3
T7commit/rollbackCommit/rollback Transaction A

Nested Transaction (NESTED)​

Create a nested scope using a Savepoint on the current transaction. Nested rollback does not end the outer transaction; outer rollback also undoes nested work.

  • Constant Propagation.NESTED
Roll Back Only the Coupon Work on Failure
txTemplate.execute(tranStatus -> {
couponMapper.bindCoupon(orderId, couponId);
return null;
}, Propagation.NESTED);

NESTED requires database and driver Savepoint support. Use it when a local step may fail within a larger transaction.

TimeTransaction ATransaction BEffect
T1beginBegin Transaction A
T2insert data1
T3beginCreate Savepoint B
T4insert data2
T5commit/rollbackRelease/rollback Savepoint B
T6insert data3
T7commit/rollbackCommit/rollback Transaction A

Follow Current Context (SUPPORTS)​

Run non-transactionally when no transaction exists; otherwise join it, as with REQUIRED.

  • Constant Propagation.SUPPORTS
Queries Follow the Caller Context
@Transactional(propagation = Propagation.SUPPORTS)
public OrderInfo queryOrder(long orderId) {
return orderMapper.queryOrder(orderId);
}
info

SUPPORTS neither starts a transaction nor prevents one.

Non-Transactional (NOT_SUPPORTED)​

Run non-transactionally; suspend any existing transaction first.

  • Constant Propagation.NOT_SUPPORTED
Run Large Queries Outside the Outer Transaction
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public List<OrderReport> queryReport() {
return reportMapper.queryOrderReport();
}
TimeTransaction ATransaction BEffect
T1beginBegin Transaction A
T2insert data1
T3beginSuspend Transaction A
T4insert data2Run non-transactionally
T5commit/rollbackResume Transaction A
T6insert data3
T7commit/rollbackCommit/rollback Transaction A

Exclude Transactions (NEVER)​

Run non-transactionally if no transaction exists; otherwise throw an exception.

  • Constant Propagation.NEVER
Forbid Calls Within a Transaction
@Transactional(propagation = Propagation.NEVER)
public void rebuildSearchIndex() {
...
}

Require a Transaction (MANDATORY)​

Join an existing transaction; throw an exception if none exists.

  • Constant Propagation.MANDATORY
Require an Outer Transaction
@Transactional(propagation = Propagation.MANDATORY)
public void insertOrderItem(long orderId, long skuId) {
orderMapper.insertOrderItem(orderId, skuId);
}

Propagation Comparison​

PropagationNo TransactionExisting Transaction
REQUIREDBegin newJoin existing
REQUIRES_NEWBegin newSuspend existing → Begin new
NESTEDBegin newSavepoint nested scope
SUPPORTSRun non-transactionallyJoin existing
NOT_SUPPORTEDRun non-transactionallySuspend existing → Run non-transactionally
NEVERRun non-transactionallyThrow exception
MANDATORYThrow exceptionJoin existing

Further Reading​