SoFunction
Updated on 2025-04-06

How to use the @Transactional of Spring boot for transaction management

In Spring Boot,@TransactionalIt is a key annotation for declarative transaction management. It is based on Spring's AOP (System-Oriented Programming) implementation, which can simplify the management of database transactions.

1. Prerequisites

  • Dependency introduction: Make sure the project containsspring-boot-starter-data-jpaorspring-boot-starter-jdbc
  • Enable Transaction Management: Spring Boot automatically configures the transaction manager by default (such asDataSourceTransactionManager), no need to enable it manually.

2. Basic usage

1. Add annotations to the method

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    @Transactional
    public void createUser(User user) {
        (user);
        // Other database operations    }
}

2. Add annotation on the class

@Service
@Transactional
public class UserService {
    // All public methods in the class apply transactions}

3. Core configuration parameters

1. Propagation behavior

Control the boundaries of transactions, default to

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateUser(User user) {
    // Always start new transactions}

Common options:

  • REQUIRED(Default): Join if a transaction currently exists; otherwise, create a new one.
  • REQUIRES_NEW: Always create a new transaction and suspend the current transaction (if any)
  • SUPPORTS: Join if a transaction currently exists; otherwise, non-transaction runs
  • NOT_SUPPORTED: Non-transaction running, suspend the current transaction (if any)
  • MANDATORY: Must be called in the transaction, otherwise an exception will be thrown
  • NEVER: Must be called in non-transaction, otherwise an exception will be thrown

2. Isolation level

Control the isolation of transactions, default to(Use database default).

@Transactional(isolation = )
public void sensitiveOperation() {
    // Maximum isolation level}

3. Timeout

Transaction timeout (seconds), default -1 (using database default).

@Transactional(timeout = 30)
public void longRunningProcess() {
    // Will roll back after more than 30 seconds}

4. ReadOnly mode

Optimized read-only operation, defaultfalse

@Transactional(readOnly = true)
public List<User> findAllUsers() {
    return ();
}

5. RollbackFor/noRollbackFor

Specify the type of exception that triggers the rollback:

@Transactional(rollbackFor = )
public void process() throws CustomException {
    // Roll back when throwing CustomException}

4. Key points to note

1. Method Visibility

Must bepublic: Due to Spring AOP implementation mechanism, non-public methods@Transactionalinvalid

2. Self-call problem

When methods in the same class are called each other, the transaction will not take effect (bypass the proxy object)

// Error examplepublic void methodA() {
    methodB(); // The transaction does not take effect}
@Transactional
public void methodB() { ... }

3. Exception handling

  • Default rollback condition: throwRuntimeExceptionorError
  • Checked Exception will not trigger rollback by default
// Handle check-type exceptions@Transactional(rollbackFor = )
public void fileOperation() throws IOException {
    // ...
}

4. Multi-data source transactions

Multiple transaction managers need to be configured and passed@Transactional(value = "specificTransactionManager")Specify

5. Debugging skills

Enable debug logging:

=TRACE
=DEBUG

Check the proxy object:

(().getName());
// The output should be a proxy class:.$ProxyXX or ...$$EnhancerBySpringCGLIB$$...

6. Best Practices

  • Service layer usage: Use transactions in the Service layer instead of the Controller layer
  • Keep short transactions: Avoid time-consuming operations such as remote calls, file IO, etc. in transactions
  • Precise rollback rules: Clearly specifiedrollbackForInstead of relying on default behavior
  • Use in combination with JPA
@Transactional
public void updateUserEmail(Long userId, String email) {
    User user = (userId).orElseThrow();
    (email); // Automatic dirty checking, update when transaction commits}

7. Complete example

@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    @Autowired
    private InventoryService inventoryService;
    @Transactional(
        propagation = ,
        isolation = Isolation.READ_COMMITTED,
        timeout = 30,
        rollbackFor = {, }
    )
    public void placeOrder(Order order) {
        (()); // InsufficientStockException may be thrown        (order);
        processPayment(order); // PaymentFailedException may be thrown    }
    private void processPayment(Order order) {
        // Payment logic    }
}

8. Suitable for relational databases

@TransactionalThe use of the database layer is directly related to the database layer, but its transaction management mechanism is based on database transactions. If the Service layer method uses Elasticsearch client read and write Elasticsearch, then@Transactionalbehavior will be affected.

1.@TransactionalScope of application

  • @TransactionalIt is a transaction management mechanism provided by Spring, mainly used for managementDatabase transactions
  • It depends on the underlying transaction manager (e.g.DataSourceTransactionManager), and these transaction managers usually interact with relational databases (such as MySQL, PostgreSQL).

Database transactions@TransactionalIt ensures atomicity, consistency, isolation and persistence (ACID) of database operations.Non-database operations: For non-database operations (such as Elasticsearch, Redis, file system, etc.),@TransactionalIt cannot directly manage its transactions.

and@TransactionalThe relationship

Elasticsearch is a distributed search engine that does not support transactions (ACIDs) in the traditional sense. therefore,@TransactionalThere is no direct transaction management capability for Elasticsearch operations.

(1) Scenario Analysis

Suppose our Service method operates both the database and Elasticsearch:

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository; // Database operation    @Autowired
    private ElasticsearchClient elasticsearchClient; // Elasticsearch operation    @Transactional
    public void createUser(User user) {
        // Operate the database        (user);
        // Operation Elasticsearch        IndexRequest request = new IndexRequest("users")
            .id(().toString())
            .source("name", (), "email", ());
        (request, );
    }
}

(2) Possible problems

  • Database transactions rollback, Elasticsearch operations do not rollback:
  • If (user) succeeds, but subsequent Elasticsearch operations fail, the database transaction will roll back (because @Transactional takes effect), but the data of Elasticsearch has been written and cannot be rolled back.
  • Database transaction commits, Elasticsearch operation fails:
  • If the database operation is successful, but the Elasticsearch operation fails, the database transaction has been committed, and the Elasticsearch data is not updated, resulting in inconsistent data.

3. How to solve the transaction consistency problem between Elasticsearch and database

Since Elasticsearch does not support transactions, other mechanisms need to be adopted to ensure data consistency.

(1) Manual compensation mechanism When the Elasticsearch operation fails, manually roll back the database operation.

Example:

@Transactional
public void createUser(User user) {
    try {
        (user); // Database operation        IndexRequest request = new IndexRequest("users")
            .id(().toString())
            .source("name", (), "email", ());
        (request, ); // Elasticsearch operation    } catch (Exception e) {
        // Elasticsearch operation failed, manually roll back the database        ().setRollbackOnly();
        throw e;
    }
}

(2) Message queue (final consistency)

  • Asynchronize Elasticsearch operations and achieve final consistency through message queues (such as Kafka, RabbitMQ).
  • Example:

    After the database operation is completed, a message is sent to the queue.

    The consumer reads messages from the queue and updates Elasticsearch.

(3) Two-stage submission (2PC)

  • Use a distributed transaction manager such as Seata to implement distributed transactions for databases and Elasticsearch.
  • This method is more complex and is usually not recommended for Elasticsearch.

(4) Local message table

  • Create a message table in the database to record the data that needs to be synchronized to Elasticsearch.
  • Synchronize data to Elasticsearch through a timing task or event listener.

Summarize

  • @TransactionalOnly valid for database transactions: It cannot manage transactions that are non-relational data stores such as Elasticsearch.
  • Consistency between Elasticsearch operations and database transactions: It needs to be achieved through compensation mechanisms, message queues, etc.
    • Design suggestions: Try to avoid mixing database and Elasticsearch operations in the same transaction.
    • Use asynchronous or final consistency schemes to ensure data consistency.

If you have more specific business scenarios, you can further discuss how to design a solution!

This is the end of this article about using Spring boot @Transactional for transaction management. For more related Spring boot @Transactional transaction management content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!