Mastering Spring Transaction Propagation: When and How Transactions Merge, Suspend, or Nest

This article explains the seven Spring transaction propagation levels, shows how nested @Transactional methods interact under each level, and provides concrete code‑level examples that illustrate when transactions are merged, suspended, started anew, or rolled back, helping developers choose the right strategy for reliable enterprise applications.

Architect
Architect
Architect
Mastering Spring Transaction Propagation: When and How Transactions Merge, Suspend, or Nest

What is Spring Transaction Propagation

In a Spring application, transaction propagation defines how a transactional method behaves when it is called from another transactional method. The propagation level determines whether the inner method joins the existing transaction, starts a new one, runs without a transaction, or throws an exception.

Propagation Levels Defined by Spring

PROPAGATION_REQUIRED

PROPAGATION_SUPPORTS

PROPAGATION_MANDATORY

PROPAGATION_REQUIRES_NEW

PROPAGATION_NOT_SUPPORTED

PROPAGATION_NEVER

PROPAGATION_NESTED

The article walks through each level, explains the underlying rules, and shows when each should be used.

How Each Propagation Works

1. REQUIRED (default)

If a transaction already exists, the method joins it; otherwise a new transaction is created.

Example (TestAService and TestBService):

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() { /* insert A */ testBService.methodB(); }

@Transactional(propagation = Propagation.REQUIRED)
public void methodB() { /* insert B */ }

Caller does not start a transaction → methodA opens Transaction‑1.

methodB joins Transaction‑1 because its propagation is REQUIRED.

Both inserts are executed within the same transaction and are committed together, or both are rolled back if any exception occurs.

2. REQUIRES_NEW

Always starts a new transaction, suspending any existing one.

Example:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() { /* insert A */ testBService.methodB(); }

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() { /* insert B */ }

methodA opens Transaction‑1.

When methodB is invoked, Transaction‑1 is suspended and Transaction‑2 is created for methodB.

Transaction‑2 commits independently; after it finishes, Transaction‑1 resumes and later commits.

If methodB throws an exception, only Transaction‑2 rolls back; Transaction‑1 can still commit provided methodA catches the exception.

3. SUPPORTS

If a transaction exists, the method joins it; otherwise it runs without a transaction.

Scenario 1 – outer method has no transaction:

@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() { /* insert B */ }

methodA (non‑transactional) calls methodB.

Because there is no surrounding transaction, methodB also executes non‑transactionally.

Scenario 2 – outer method is REQUIRED:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() { /* insert A */ testBService.methodB(); }

@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() { /* insert B */ }

methodA opens Transaction‑1.

methodB joins Transaction‑1 because a transaction is already present.

Both inserts are committed together.

4. NOT_SUPPORTED

Suspends any existing transaction and executes the method non‑transactionally.

Example:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() { /* insert A */ testBService.methodB(); }

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void methodB() { /* insert B */ }

methodA opens Transaction‑1.

When methodB is called, Transaction‑1 is suspended; methodB runs without a transaction.

After methodB finishes, Transaction‑1 resumes and later commits.

5. NEVER

Method must not run inside a transaction; if a transaction exists, an exception is thrown.

Example:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() { /* insert A */ testBService.methodB(); }

@Transactional(propagation = Propagation.NEVER)
public void methodB() { /* insert B */ }

methodA opens Transaction‑1.

Calling methodB triggers an exception because its propagation is NEVER.

Transaction‑1 is rolled back as a result of the exception.

6. MANDATORY

Method must run inside an existing transaction; otherwise an exception is thrown.

Example when no outer transaction exists:

@Transactional(propagation = Propagation.MANDATORY)
public void methodB() { /* insert B */ }

Calling methodB without a surrounding transaction causes an exception.

Example when an outer transaction exists (REQUIRED):

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() { /* insert A */ testBService.methodB(); }

@Transactional(propagation = Propagation.MANDATORY)
public void methodB() { /* insert B */ }

methodA opens Transaction‑1.

methodB joins Transaction‑1 because a transaction is already present.

Both operations are committed together.

7. NESTED

Creates a savepoint within the existing transaction. If the nested method rolls back, only the work after the savepoint is undone.

Example:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() { /* insert A */ testBService.methodB(); }

@Transactional(propagation = Propagation.NESTED)
public void methodB() { /* insert B */ }

methodA opens Transaction‑1 (or joins an existing one).

methodB creates a savepoint inside Transaction‑1.

If methodB throws an exception, only the changes after the savepoint are rolled back; methodA can still commit.

Summary

The article clarifies how Spring’s seven transaction propagation options affect nested @Transactional calls, demonstrates each behavior with concrete code snippets and execution flow diagrams, and explains the impact on commit and rollback semantics. Understanding these rules enables developers to design reliable, atomic business operations in enterprise‑level Java back‑end systems.

Author: Zhou Shaobin (source: vivo互联网技术)

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavatransactionspringtransactionalpropagationSpring Framework
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.