skip to content

Propagation Behaviors

What happens when one transactional method calls another: REQUIRED, REQUIRES_NEW, NESTED, and the SUPPORTS/MANDATORY/NEVER family. Propagation scenarios are the most reliable way for an interviewer to find out whether you really understand transactions.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

23

What does Propagation.NOT_SUPPORTED do in Spring's declarative transaction management?

level: juniorimportance: must knowfreq 45%

answer

  1. Suspend outer tx, run non-transactionally, resume
  2. Same suspend/resume machinery as REQUIRES_NEW but starts nothing
  3. Long read-only / reporting calls
  4. Not atomic with caller
  5. Proxy self-invocation still applies

basics

~10 s

NOT_SUPPORTED runs the method without a transaction. If a transaction is already active, Spring pauses (suspends) it, runs the method non-transactionally, then resumes the paused transaction afterward.

solid answer

~40 s

NOT_SUPPORTED is a propagation mode you set via @Transactional(propagation = Propagation.NOT_SUPPORTED). It means "execute non-transactionally." If no transaction exists, the method just runs without one. If a caller already started a transaction, Spring suspends that transaction for the duration of the call, runs the method with no active transaction bound to the thread, then resumes the outer transaction when the method returns. It is typically used for long read-only or reporting queries where you do not want to hold a database transaction (and its locks/connection) open. Because there is no transaction inside, a failure inside won't roll back the outer transaction, and the outer rollback won't undo work done here.

code

java · 15 lines
java
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class ReportService {

    // Runs with NO active transaction. If the caller had one,
    // it is suspended for the duration of this method and resumed after.
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public Report buildHeavyReport(long tenantId) {
        // Long-running aggregation query -- we intentionally do not
        // hold the caller's transaction (and its locks) open here.
        return reportDao.aggregate(tenantId);
    }
}

go deeper

for a junior

Know the one-line behavior: suspend the current transaction, run without one, resume.

for a middle

Contrast with SUPPORTS and NEVER; know it isn't atomic with the caller.

for a senior

Explain suspend/resume via AbstractPlatformTransactionManager and the visibility/connection implications.

for a principal

Reason about when suspending a slow read is worth it vs. connection-pool and consistency trade-offs.

## What propagation means In Spring, **propagation** controls what happens to transactions when one `@Transactional` method calls another. Spring's `Propagation` enum (in `org.springframework.transaction.annotation`) has seven values: `REQUIRED` (default), `REQUIRES_NEW`, `NESTED`, `SUPPORTS`, `NOT_SUPPORTED`, `MANDATORY`, and `NEVER`. ## NOT_SUPPORTED specifically `Propagation.NOT_SUPPORTED` means: **run this method with no transaction active on the thread.** Two cases: - **No active transaction on entry:** the method simply runs non-transactionally. Same effective behavior as if there were no `@Transactional` at all. - **A transaction is already active (started by a caller):** Spring **suspends** that transaction — it unbinds the transactional resources (e.g. the JDBC `Connection`) from the current thread via `TransactionSynchronizationManager` — executes your method with nothing bound, then **resumes** the suspended transaction after your method returns (or throws). ## The mechanism under the hood The orchestration lives in `AbstractPlatformTransactionManager`. When a `NOT_SUPPORTED` boundary is entered while a transaction exists, the manager calls its internal `suspend(...)`, which captures a `SuspendedResourcesHolder` (the unbound connection, synchronizations, isolation info, read-only flag, name). After the method body completes, `resume(...)` re-binds those resources to the thread. This is the same suspend/resume machinery `REQUIRES_NEW` uses — the difference is `REQUIRES_NEW` starts a *new* transaction after suspending, while `NOT_SUPPORTED` starts *nothing*. ## Consequences you must understand - **Not atomic with the caller:** work done inside NOT_SUPPORTED is not part of the outer transaction. If the outer transaction later rolls back, anything the NOT_SUPPORTED method already did is *not* undone (it was never in that transaction). - **Separate connection / visibility:** inside NOT_SUPPORTED, database reads run outside the outer transaction. They typically use a fresh connection obtained per statement (auto-commit mode via the `DataSource`), so they will **not** see the outer transaction's uncommitted changes. - **Requires a manager that supports suspension:** `DataSourceTransactionManager`, `JpaTransactionManager`, and `JtaTransactionManager` all support suspend/resume. A manager that cannot suspend throws `TransactionSuspensionNotSupportedException` if asked to. ## When to use it The canonical use case is a **long-running read-only or reporting query** invoked from within a broader transaction, where you deliberately do *not* want to keep the outer transaction (and its locks and pinned connection) open while the slow read runs. ## Gotchas - **Proxy self-invocation:** like all `@Transactional` propagation, NOT_SUPPORTED only takes effect when the call goes through the Spring proxy. Calling the method from within the same bean (`this.method()`) bypasses the proxy, so no suspension happens. - **Confusing it with NEVER:** `NEVER` *throws* if a transaction exists; `NOT_SUPPORTED` *suspends and continues*. `SUPPORTS` runs in the existing transaction if there is one but does not start a new one — it does not suspend.

  • How is NOT_SUPPORTED different from REQUIRES_NEW?
    Both suspend an existing transaction, but REQUIRES_NEW then starts a brand-new independent transaction (committed/rolled back on its own), while NOT_SUPPORTED starts no transaction at all and runs non-transactionally.
  • If the NOT_SUPPORTED method throws, does the outer transaction roll back?
    Not because of the suspension itself — the exception simply propagates. Whether the outer transaction rolls back depends on how the caller handles the propagated exception, per the caller's own rollback rules. The inner method had no transaction to roll back.

saying these in an interview costs you the question

  • Saying NOT_SUPPORTED starts a new transaction (that's REQUIRES_NEW)
  • Saying it throws when a transaction exists (that's NEVER)
  • Assuming inner reads see the outer transaction's uncommitted writes

context

open as a page

What is Propagation.REQUIRED in Spring's @Transactional, and how does it decide whether to start a new transaction?

level: juniorimportance: must knowfreq 80%

basics

~10 s

REQUIRED is the default propagation. If a transaction is already running, the method joins it; if none exists, Spring starts a new one. So the code always runs inside exactly one transaction.

open as a page

What does Propagation.REQUIRES_NEW do in Spring's @Transactional?

level: juniorimportance: must knowfreq 72%

basics

~10 s

REQUIRES_NEW always starts a brand-new, independent transaction. If a transaction is already running, Spring pauses (suspends) it, runs the new one on its own database connection, then resumes the old one.

open as a page

In Spring, what do the SUPPORTS, MANDATORY, and NEVER transaction propagation modes each do?

level: juniorimportance: must knowfreq 70%

basics

~20 s

SUPPORTS joins an existing transaction if one is active, otherwise runs without one. MANDATORY requires an active transaction and throws if none exists. NEVER requires that no transaction is active and throws if one exists.

open as a page

Compare Propagation.NESTED and Propagation.REQUIRES_NEW. When would you choose one over the other?

level: middleimportance: must knowfreq 60%

basics

~20 s

REQUIRES_NEW opens a separate transaction that commits on its own, surviving an outer rollback. NESTED stays inside one transaction using a savepoint; nested work is lost if the outer rolls back. Use REQUIRES_NEW for truly independent commits, NESTED for partial rollback within one unit.

open as a page

Under REQUIRED, an inner method throws and rolls back but the outer catches the exception and continues. What happens at commit, and why?

level: seniorimportance: must knowfreq 70%

basics

~10 s

The inner failure marks the shared transaction rollback-only. Even though the outer swallowed the exception, when it tries to commit Spring refuses and throws UnexpectedRollbackException — the whole transaction rolls back.

open as a page

A developer annotates a helper method with REQUIRES_NEW and calls it from another method in the same class, but no separate transaction is created. Why, and how do you fix it?

level: seniorimportance: must knowfreq 63%

basics

~20 s

Spring applies @Transactional through a proxy that wraps the bean. An internal call (this.method()) skips the proxy, so the transaction advice never runs. Fix it by calling through an injected bean reference so the call goes through the proxy.

open as a page

What does Propagation.NESTED do in Spring transaction management, and how is it different from a completely separate transaction?

level: juniorimportance: should knowfreq 45%

basics

~10 s

NESTED runs inside the existing transaction but sets a savepoint first. If the nested part fails, Spring rolls back only to that savepoint, keeping the outer work. It is one physical transaction, not two.

open as a page

How does NOT_SUPPORTED differ from SUPPORTS and NEVER when a transaction is or isn't already active?

level: middleimportance: should knowfreq 38%

basics

~20 s

SUPPORTS joins an existing transaction but starts none if there isn't one. NEVER throws if a transaction exists, else runs without one. NOT_SUPPORTED always runs without one — suspending any existing transaction rather than joining or failing.

open as a page

With nested REQUIRED calls, what is shared between the outer and inner methods, and what is the difference between a physical and a logical transaction?

level: middleimportance: should knowfreq 55%

basics

~20 s

They share one physical transaction and one JDBC Connection. Each @Transactional method is a separate logical scope, but nested REQUIRED scopes all map onto the single physical transaction, so they commit or roll back together.

open as a page

How does REQUIRES_NEW differ from Propagation.NESTED?

level: middleimportance: should knowfreq 55%

basics

~20 s

REQUIRES_NEW is a fully independent transaction on its own connection that commits separately. NESTED is a savepoint inside the same transaction and connection — rolling it back undoes only to the savepoint, but it commits only when the outer transaction commits.

open as a page

When would you use MANDATORY propagation, and what happens if the method is called outside a transaction?

level: middleimportance: should knowfreq 55%

basics

~10 s

Use MANDATORY when a method must only run inside an existing transaction opened by its caller. If called with no active transaction, Spring throws IllegalTransactionStateException and the method body never runs.

open as a page

Why does Propagation.NESTED often fail with JPA/Hibernate, and what transaction manager makes it work?

level: seniorimportance: should knowfreq 40%

basics

~10 s

NESTED needs JDBC savepoints. JpaTransactionManager does not support them by default, so it throws NestedTransactionNotSupportedException. Use DataSourceTransactionManager (or JdbcTransactionManager), which builds on plain JDBC connections and supports savepoints.

open as a page

Explain the suspend/resume mechanism behind NOT_SUPPORTED and its effect on data visibility and connections.

level: seniorimportance: should knowfreq 30%

basics

~20 s

Spring's transaction manager unbinds the active connection from the thread (suspend), runs the method with a fresh non-transactional connection, then rebinds the original (resume). Reads inside can't see the outer transaction's uncommitted changes because they use a different connection.

open as a page

When would you choose REQUIRES_NEW over REQUIRED, and what does that change about connections and commit boundaries?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Use REQUIRES_NEW when a piece of work must commit or roll back independently of the caller — like an audit log that should persist even if the main transaction fails. It suspends the current transaction and runs on a second, separate Connection.

open as a page

Explain the resource-suspension mechanics when REQUIRES_NEW is entered inside an existing transaction.

level: seniorimportance: should knowfreq 48%

basics

~20 s

Spring unbinds the outer transaction's Connection from the thread (stored in TransactionSynchronizationManager), obtains a new Connection for the inner transaction, runs it, then rebinds and resumes the outer one after the inner commits or rolls back.

open as a page

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

level: seniorimportance: should knowfreq 40%

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.

open as a page

How does defaulting to REQUIRED shape where you place transaction boundaries in a layered/modular service architecture, and what pitfalls arise from the shared-transaction model at scale?

level: principalimportance: should knowfreq 35%

basics

~20 s

Because REQUIRED joins any active transaction, the boundary is set by the outermost @Transactional call — usually the top-level service method (the unit of work). Everything it calls shares that transaction, so keep those methods focused and avoid slow I/O inside them.

open as a page

You add REQUIRES_NEW to an audit method called deep inside high-throughput request transactions. What production risks must you evaluate, and how would you mitigate them?

level: principalimportance: should knowfreq 34%

basics

~20 s

Each request now holds two connections at once, so the pool can exhaust or deadlock under load. Also the inner tx can't see the outer's uncommitted rows/locks and may block. Mitigate by sizing the pool for nesting depth, keeping the inner tx tiny, or moving audit to an async/outbox path.

open as a page

What are the subtle risks of using SUPPORTS when no transaction is active?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

With SUPPORTS and no active transaction, the method runs without one, so there is no rollback, isolation is the connection default, and any read-only/isolation settings on the annotation are effectively ignored. Behavior differs depending on the caller.

open as a page

You need per-item partial rollback in a large batch. Discuss the trade-offs of Propagation.NESTED (savepoints) versus alternatives at scale, including locking and failure semantics.

level: principalimportance: nice to knowfreq 22%

basics

~20 s

NESTED gives cheap per-item rollback via savepoints within one transaction, but the whole batch is still one long transaction holding locks and one connection. Alternatives — chunked commits, REQUIRES_NEW per item, or idempotent retries — trade atomicity for shorter locks and independent durability.

open as a page

When would you choose NOT_SUPPORTED for a reporting call, and what design trade-offs and pitfalls must you weigh?

level: principalimportance: nice to knowfreq 18%

basics

~10 s

Use NOT_SUPPORTED for a long read-only/reporting query called from within a transaction, so you don't hold the caller's transaction, locks, and connection open. Weigh connection-pool doubling, lost read-your-writes visibility, and proxy self-invocation.

open as a page

How would you use MANDATORY and NEVER to enforce transactional boundaries in a layered architecture, and what limits this enforcement?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Mark low-level operations MANDATORY so they must run inside a caller's transaction, and mark operations that must stay outside one NEVER. Both fail fast with IllegalTransactionStateException. The main limit: the checks only fire through the Spring proxy, so self-invocation bypasses them.

open as a page