skip to content

What does NEVER propagation guarantee, and how does it differ from NOT_SUPPORTED?

level: seniorimportance: should knowfreq 40%

answer

  1. NEVER = must have NO tx, else throw
  2. NOT_SUPPORTED = run outside tx, suspend if present
  3. NEVER throws vs NOT_SUPPORTED suspends
  4. NEVER is the mirror of MANDATORY
  5. both run non-transactionally

basics

~10 s

NEVER guarantees the method runs with no active transaction, throwing IllegalTransactionStateException if one exists. NOT_SUPPORTED also runs non-transactionally but tolerates an existing transaction by suspending it instead of throwing.

solid answer

~50 s

NEVER is a guard: the method must not execute inside a transaction. If none is active it runs normally (non-transactionally); if a transaction is active the interceptor throws org.springframework.transaction.IllegalTransactionStateException ('Existing transaction found for transaction marked with propagation never'). NOT_SUPPORTED has the same end state — code runs without a transaction — but it is permissive: when a transaction is active it suspends it, runs the body outside it, then resumes it afterward. So NEVER is an assertion that fails loudly, while NOT_SUPPORTED is an accommodation that silently sidesteps. Use NEVER to catch a design violation (this code must never be in a tx); use NOT_SUPPORTED when you legitimately want to escape an ambient transaction, e.g. for a long-running read or a call you don't want holding a DB connection/lock. Both require the transaction manager to support suspension for the NOT_SUPPORTED case.

code

java · 12 lines
java
@Service
public class ExternalOps {

    // Must NOT be inside a tx: throws if a caller opened one.
    @Transactional(propagation = Propagation.NEVER)
    public void strictNonTxCall() { externalSystem.push(); }

    // Tolerant: if a tx is active it is suspended, this runs
    // outside it, then the tx resumes afterward.
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public Report longRunningReport() { return reportDao.build(); }
}

go deeper

for a junior

Know NEVER throws if a transaction is active.

for a middle

State that both run non-transactionally and that NEVER throws while NOT_SUPPORTED suspends.

for a senior

Explain suspend/resume mechanics and pick the right one for escaping an ambient transaction.

for a principal

Reason about lock/connection-holding tradeoffs and when strict NEVER guards belong in an architecture vs pragmatic NOT_SUPPORTED escapes.

**NEVER** = `@Transactional(propagation = Propagation.NEVER)`. It asserts the **absence** of a transaction. - No active transaction → method runs non-transactionally (the intended path). - Active transaction → interceptor throws `org.springframework.transaction.IllegalTransactionStateException` (*"Existing transaction found for transaction marked with propagation 'never'"*). The body does not run. NEVER never suspends anything — it only validates. It is symmetric to MANDATORY: MANDATORY demands a transaction, NEVER forbids one. **NOT_SUPPORTED** = `Propagation.NOT_SUPPORTED`. It also results in the body running **non-transactionally**, but it is tolerant: - No active transaction → runs non-transactionally. - Active transaction → **suspends** it (via `PlatformTransactionManager` / `TransactionSynchronizationManager`), runs the body outside any transaction, then **resumes** the suspended transaction when the method returns. **Key difference:** presence of an ambient transaction. NEVER treats it as an error and aborts; NOT_SUPPORTED treats it as something to step around and continues. Choosing between them is about intent: - Use **NEVER** when running inside a transaction would be a *bug* you want surfaced immediately — a strict contract that this operation must always be outside transactional scope. - Use **NOT_SUPPORTED** when you *expect* callers may be transactional but this particular work should not participate — e.g. a long-running report query you don't want holding write locks, calling a slow external service you don't want prolonging a DB transaction, or bulk operations you want auto-committed independently. **Mechanism & caveats:** - Both rely on the thread-bound transaction state and the AOP proxy; **self-invocation bypasses** them. - NOT_SUPPORTED requires the transaction manager to support suspend/resume (JpaTransactionManager and DataSourceTransactionManager do). - With NEVER (or NOT_SUPPORTED) running non-transactionally, there is no rollback semantics and isolation is the connection default — don't rely on `@Transactional` guarantees inside such a method. - A frequent mistake: expecting NEVER to 'suspend' the transaction like NOT_SUPPORTED. It does not; it throws.

  • If you want to run a slow external call from inside a transactional service without holding the DB transaction open, which propagation fits — NEVER or NOT_SUPPORTED?
    NOT_SUPPORTED — it suspends the active transaction, runs the call outside it, and resumes afterward. NEVER would throw because a transaction is active.

saying these in an interview costs you the question

  • Saying NEVER suspends the existing transaction (that's NOT_SUPPORTED)
  • Saying NOT_SUPPORTED throws when a transaction exists
  • Thinking NEVER starts a transaction

context