Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Understanding Spring Transaction Propagation Mechanism

Tech May 19 1

Spring transaction propagation is the core and most confusing concept in Spring transactions. It defines how transactions are passed and applied when a method with a transaction calls another method (whether it has or does not have a transaction, such as whether to create a new transaction, join an existing one, or suspend the current one).

Key premise: Propagation behavior applies only to method calls between Spring-managed beans (i.e., through proxy objects). Internal calls will still cause transaction failure (as previously mentioned).

I. Understand Core Concepts

The main issue addressed by transaction propagation: When method A (with a transaction) calls method B (with or without a transaction), how does B's transaction interact with A's transaction?

  • Current transaction: The transaction context that exists in the calling method;
  • New transaction: A new independent transaction created by the called method;
  • No transaction: The called method does not use any transaction.

II. Spring Transaction Propagation Mechanism (7 Core Types)

Spring defines 7 propagation behaviors in the TransactionDefinition interface, categorized into three main types based on usage frequency and scenarios:

Propagation Behavior Constant Chinese Name Core Rules (Key!) Typical Use Cases
**REQUIRED** (default) Must have a transaction If present, join; if not, create a new one Most business scenarios (insert, delete, update)
**SUPPORTS** Supports transaction If present, join; if not, no transaction Query methods (optional transaction)
**MANDATORY** Mandatory transaction If present, join; if not, throw exception Core operations that must be executed within a transaction
**REQUIRES_NEW** Create new transaction Regardless of presence, create a new transaction, suspending the current one Logging, asynchronous notifications (independent transaction)
**NOT_SUPPORTED** Not suppported transaction Regardless of presence, execute without transaction, suspending the current one Memory-only operations (no transaction needed)
**NEVER** Prohibits transaction If present, throw exception; if not, no transaction Operations that must not run within a transaction
**NESTED** Nested transaction If present, nest (savepoint); if not, create new Sub-transaction not affected by main transaction rollback (rarely used)

Detailed Explanation (With Code Examples)

1. Most Common: REQUIRED (Default)

Core Logic: If the caller has a transaction, it joins; otherwise, it creates a new one. This is the default propagation behavior in Spring and is chosen in 90% of daily development scenarios.

@Service
public class OrderService {
    @Autowired
    private StockService stockService;

    // Caller: has transaction
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder() {
        // 1. Save order (current transaction)
        saveOrder();
        // 2. Call inventory service (join current transaction)
        stockService.reduceStock();
        // 3. Any step fails, entire transaction rolls back
    }
}

@Service
public class StockService {
    // Called method: REQUIRED (default)
    @Transactional
    public void reduceStock() {
        updateStock(); // Executed within the same transaction as creating the order
    }
}

Effect:

  • If createOrder runs first (with a transaction), reduceStock joins that transaction, both succeed/fail together;
  • If reduceStock is called directly (without a transaction), a new transaction is created for execution.

2. Independent Transaction: REQUIRES_NEW

Core Logic: Regardless of whether the caller has a transaction, a new independent transaction is created, and the current trnasaction is suspended. After the new transaction completes, the original transaction resumes. This is suitable for scenarios where the sub-operation must be committed even if the main transaction fails (e.g., logging operations).

@Service
public class OrderService {
    @Autowired
    private LogService logService;

    // Main transaction
    @Transactional
    public void createOrder() {
        saveOrder(); // Main transaction operation
        try {
            // Call log service (new independent transaction)
            logService.recordLog("Creating order");
        } catch (Exception e) {
            // Log recording failure does not affect the main transaction
        }
        // Main transaction rolls back, log transaction has already been committed and not rolled back
    }
}

@Service
public class LogService {
    // New independent transaction
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void recordLog(String content) {
        insertLog(content); // Independent transaction, unrelated to the main transaction
    }
}

Key Difference (REQUIRED vs REQUIRES_NEW):

Scenario REQUIRED REQUIRES_NEW
Main transaction rollback Sub-operations also roll back Sub-operations have already been committed and do not roll back
Transaction isolation Same transaction, shared locks/data Independent transaction, no impact on each other

3. Other Common Propagation Behaviors

  • SUPPORTS: If there is a transaction, join it; if not, no transaction. Suitable for query methods (e.g., getOrderById), using it if the outer layer has a transaction, but can also execute without it;
  • MANDATORY: Must be executed within a transaction, otherwise throws IllegalTransactionStateException. For example, core financial operations, which require the outer layer to have a transaction;
  • NOT_SUPPORTED: Execute without a transaction regardless of whether there is one. For example, pure cache operations, which do not need a transaction;
  • NEVER: Prohibits transactions, throwing an exception if present. For example, certain sensitive non-transactional operations;
  • NESTED: Nested transaction (based on database savepoints), where the main transaction rolling back to a savepoint allows the sub-transaction to roll back independently. Rarely used (depends on database support, such as MySQL InnoDB).

III. Classic Scenario Comparison (Help You Choose)

Business Scenario Recommended Propagation Behavior Reason
Order creation + inventory deduction REQUIRED Both succeed/fail to gether
Order creation + operation log recording REQUIRES_NEW Log must be saved, unaffected by the main transaction
Querying order details only SUPPORTS Use transaction if available, otherwise query directly
Core fund transfer operation MANDATORY Requires the outer layer to have a transaction, avoid unsecured execution
Pure memory calculation/cache update NOT_SUPPORTED No transaction needed, improves performance

IV. Pitfalls to Avoid

  1. Propagation mechanism only works for proxy calls: Internal calls within the same class (e.g., A.method1() calling A.method2()) will not trigger the propagation mechanism, still following the rule of "internal calls leading to transaction failure";
  2. REQUIRES_NEW may cause locking issues: Creating a new transaction holds a database lock. If the main transaction takes too long, it may lead to lock waiting;
  3. Nested transactions (NESTED) depend on the database: Only supported by databases with savepoints (such as MySQL/Oracle), and Spring's support for it is limited. Prefer using REQUIRES_NEW instead;
  4. Exception handling affects propagation effect: If the sub-transaction throws an exception but is caught by the main transaction, manually set rollback (TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()) to avoid inconsistent transaction states.

V. Practical Verification Methods

To verify whether the propagation mechanism is effective, two methods are recommended:

  1. Log debugging: Enable Spring transaction logs to view logs about transaction creation, suspension, and commit:
<!-- logback configuration -->
<logger name="org.springframework.transaction" level="DEBUG"/>
<logger name="org.springframework.jdbc.datasource.DataSourceTransactionManager" level="DEBUG"/>

  1. Database lock verification: Add Thread.sleep(10000) in the method, and check the transaction status of the database connection using show processlist.
Tags: spring

Related Articles

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

SBUS Signal Analysis and Communication Implementation Using STM32 with Fus Remote Controller

Overview In a recent project, I utilized the SBUS protocol with the Fus remote controller to control a vehicle's basic operations, including movement, lights, and mode switching. This article is aimed...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.