Spring Transaction Management

Published 2026-07-30 19:36 Updated 2026-07-30 19:36 1992 words 10 min read ... Page views

This article systematically introduces the core mechanisms and practical points of Spring transaction management. It focuses on the PlatformTransactionManager interface and its use in scenarios such as JDBC, JPA, Hibernate, and JTA. It explains in detail the meaning and configuration of transaction attributes (such as propagation behavior, isolation level, read-only flag, and timeout), and analyzes transaction status, rollback rules and common failure scenarios. At the same time, it compares the implementation methods of programmatic and declarative transactions, and proposes that transactions should be reasonably configured at the Service layer, avoid long-term operations, and choose propagation behavior based on business semantics. It also emphasizes the need to ensure that rollback rules take effect when handling exceptions.

Spring Transaction Management

The relationship between Spring transaction management related interfaces is as follows:

image-001
image-001

Transaction management overview

Spring provides a unified transactional programming model for different data access technologies such as JDBC, JPA, Hibernate, and JTA. Business code is mainly oriented towards Spring’s transaction abstraction, while specific commit, rollback, and resource management are completed by the corresponding transaction manager.

The core interface is PlatformTransactionManager:

public interface PlatformTransactionManager extends TransactionManager {
    TransactionStatus getTransaction(TransactionDefinition definition)
            throws TransactionException;

    void commit(TransactionStatus status) throws TransactionException;

    void rollback(TransactionStatus status) throws TransactionException;
}

getTransaction() obtains the current transaction status according to the transaction definition; commit() and rollback() are responsible for committing and rolling back respectively.

Common transaction managers

JDBC transaction

When using JDBC or JdbcTemplate, DataSourceTransactionManager is usually used.

<bean id="transactionManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

It manages transactions through JDBC Connection obtained from the same data source, and ultimately calls connected commit() or rollback().

JPA Affairs

When using JPA, JpaTransactionManager is usually used.

<bean id="transactionManager"
      class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

JpaTransactionManager collaborates with JPA’s EntityManagerFactory and EntityManager to manage affairs.

Hibernate transactions

When using native Hibernate SessionFactory directly, you can use HibernateTransactionManager. If the project is implemented using Hibernate via JPA, JpaTransactionManager should be preferred.

<bean id="transactionManager"
      class="org.springframework.orm.hibernate5.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

JTA transaction

When a business operation needs to coordinate multiple transactional resources, JTA and JtaTransactionManager can be used. For example, a transaction involves multiple databases or databases and messaging systems simultaneously.

<bean id="transactionManager"
      class="org.springframework.transaction.jta.JtaTransactionManager"/>

The specific configuration of JTA depends on the application server or the transaction coordinator used. You cannot rely on a fixed Bean configuration to adapt to all environments.

transaction attribute

TransactionDefinition is used to describe transaction policies, which mainly includes propagation behavior, isolation level, timeout time, read-only flag and transaction name.

image-002
image-002
public interface TransactionDefinition {
    int getPropagationBehavior();

    int getIsolationLevel();

    int getTimeout();

    boolean isReadOnly();

    String getName();
}

transaction communication behavior

Propagation behavior determines whether a method with a transaction should join an existing transaction, create a new transaction, or prohibit the transaction when it is called by another transaction method.

Communication BehaviorMeaning
REQUIREDdefault value. If there is a transaction, join it, and if there is no transaction, create a new transaction.
SUPPORTSjoins if there is a transaction, and runs in a non-transactional manner if there is no transaction.
MANDATORYmust have a transaction, otherwise an exception will be thrown.
REQUIRES_NEWalways creates new transactions; if there are already transactions, outer transactions are suspended.
NOT_SUPPORTEDruns non-transactional; if there is a transaction, the transaction is suspended.
NEVERmust run non-transactional; if a transaction exists, an exception is thrown.
NESTEDuses savepoints to form nested ranges when there is a transaction; the behavior is similar to REQUIRED when there is no transaction.

REQUIRED

Multiple methods join the same physical transaction, where any participant marks the transaction as rollback only, and the entire transaction is rolled back when it is finally committed.

@Transactional(propagation = Propagation.REQUIRED)
public void saveOrder() {
    // 业务逻辑
}

REQUIRES_NEW

Inner methods use independent transactions, and outer transactions are suspended during the execution of the inner transaction. After an inner transaction commits, even if the outer transaction subsequently rolls back, the inner committed content usually does not roll back.

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveAuditLog() {
    // 独立保存审计日志
}

NESTED

NESTED is usually implemented based on JDBC savepoints in the same physical transaction. The inner scope can be rolled back to the savepoint, but it cannot be finally committed separately from the outer transaction like an independent transaction; when the outer transaction is rolled back, the nested scope will also be rolled back.

This propagation behavior relies on the transaction manager and underlying resource support for savepoints, and is common in JDBC resource transactions.

transaction isolation level

Isolation levels are used to control data visibility between concurrent transactions.

Common questions about concurrent transactions

  • Dirty read: Read data that has not yet been committed by other transactions.
  • Non-repeated read: Reading the same row twice within the same transaction results in different values, usually caused by updates and committing by other transactions.
  • Magic read: When querying twice in the same transaction under the same conditions, the number of rows in the result set changes, usually caused by the insertion or deletion of other transactions and committing.

Spring isolation level

Isolation LevelMeaning
DEFAULTuses the database default isolation level.
READ_UNCOMMITTEDallows reading of uncommitted data, which may cause dirty reading, non-repeatable reading and phantom reading.
READ_COMMITTEDcan only read submitted data to avoid dirty reading.
REPEATABLE_READKeep consistent when reading read rows repeatedly within the same transaction to avoid dirty and non-repeatable reads.
SERIALIZABLEtransactions are executed approximately serially, with the highest isolation, but generally the lowest concurrency performance.

Different databases have different locking mechanisms and multi-version concurrency control implementations, so the isolation level of the same name may differ in specific details.

@Transactional(isolation = Isolation.READ_COMMITTED)
public void updateAccount() {
    // 业务逻辑
}

read-only transactions

The read-only flag indicates that the transaction mainly performs query operations, and the transaction manager and database can optimize accordingly.

@Transactional(readOnly = true)
public User queryById(Long id) {
    return userMapper.selectById(id);
}

readOnly = true is usually an optimization tip and should not be regarded as an absolute database write security limit. Whether writes are actually disabled depends on the transaction manager and database implementation.

transaction timeout

Transaction timeouts are used to limit the maximum time a transaction is allowed to execute. After a timeout, transactions are usually marked as rolling back.

@Transactional(timeout = 10)
public void importData() {
    // 业务逻辑
}

Avoid performing long network calls, file processing, or human waits during transactions, otherwise it may consume database connection and lock resources for a long time.

Rollback rule

Spring declarative transactions are rolled back by default when RuntimeException or Error is thrown; for ordinary checked exceptions, they are not rolled back by default.

@Transactional(rollbackFor = Exception.class)
public void transfer() throws Exception {
    // 发生受检异常时也回滚
}

You can also use noRollbackFor to specify that certain exceptions do not trigger rollback.

@Transactional(noRollbackFor = BusinessWarningException.class)
public void execute() {
    // 业务逻辑
}

Rollback rules should be set according to business semantics, and it is not recommended to silently process exceptions after indiscriminately capturing exceptions. If an exception is caught internally by a method and is no longer thrown, the transaction interceptor is usually unaware of the exception and will not automatically roll back as the exception.

transaction state

TransactionStatus represents the running state of the current transaction and provides capabilities such as savepoints and rollback only flags.

public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
    boolean hasSavepoint();

    void flush();
}

Common state methods include:

  • isNewTransaction(): Whether a new transaction has been created for the current call.
  • hasSavepoint(): Whether you hold a preservation point.
  • setRollbackOnly(): Mark transactions as rollback only.
  • isRollbackOnly(): Whether the transaction has been marked for rollback.
  • isCompleted(): Whether the transaction has been completed.

The specific method of the interface will evolve with Spring versions, and its use should be based on the API in the current project.

programmatic transaction

Programmatic transactions clearly control transaction boundaries by business code, which is suitable for scenarios where the transaction scope requires dynamic determination or fine control. Its shortcoming is that transaction code will enter business logic and is highly intrusive.

Using TransactionTemplate

TransactionTemplate is one of the recommended programmatic transaction methods.

@Service
public class UserService {
    private final TransactionTemplate transactionTemplate;
    private final UserRepository userRepository;

    public UserService(
            TransactionTemplate transactionTemplate,
            UserRepository userRepository) {
        this.transactionTemplate = transactionTemplate;
        this.userRepository = userRepository;
    }

    public Boolean save(User user) {
        return transactionTemplate.execute(status -> {
            try {
                userRepository.insert(user);
                return true;
            } catch (RuntimeException e) {
                status.setRollbackOnly();
                throw e;
            }
        });
    }
}

If the callback throws a run-time exception, the transaction template is rolled back according to the rules. setRollbackOnly() needs to be explicitly called only if you need to convert an exception to a normal return value.

Use PlatformTransactionManager directly

DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

TransactionStatus status = transactionManager.getTransaction(definition);
try {
    userRepository.insert(user);
    transactionManager.commit(status);
} catch (RuntimeException e) {
    transactionManager.rollback(status);
    throw e;
}

Operating the transaction manager directly is more flexible, but you need to ensure that the commit, rollback, and exception propagation logic are correct.

declarative transaction

Declarative transactions are based on Spring AOP, applying transaction logic to target methods through configuration or annotations, and business code does not require manual commit and rollback.

XML configuration method

<bean id="transactionManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="query*" read-only="true"/>
        <tx:method name="get*" read-only="true"/>
        <tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:pointcut id="servicePointcut"
                  expression="execution(* com.example.service..*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut"/>
</aop:config>

The tx and aop namespaces need to be declared in the root element.

annotation method

Enable transaction annotations first:

<tx:annotation-driven transaction-manager="transactionManager"/>

You can also use Java configuration:

@Configuration
@EnableTransactionManagement
public class TransactionConfig {
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

Using @Transactional in business categories or methods:

@Service
public class UserService {
    private final UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    @Transactional(rollbackFor = Exception.class)
    public void save(User user) throws Exception {
        userDao.insert(user);
    }
}

Annotations on classes serve as the default transaction configuration for methods of this class; annotations on methods can override class-level configuration.

Common failure scenarios of @Transactional

Same kind of internal call

In the default proxy mode, when a method calls another @Transactional method in its class through this, the call does not go through the Spring proxy, and the independent transaction configuration of the inner method usually does not take effect.

@Service
public class OrderService {
    public void createOrder() {
        // 这是同类内部调用,不会经过代理对象。
        saveOrder();
    }

    @Transactional
    public void saveOrder() {
        // 数据库操作
    }
}

Generally, methods that require independent transaction boundaries should be split into another Spring Bean.

Method is not proxyable

In common proxy patterns, transactions should usually be applied to exposed instance methods that can be invoked by the proxy. private methods, static methods, and object-created methods will not take effect as normal Spring Bean proxies.

The object is not a Spring Bean

Objects created manually through new are not managed by the Spring container, and their @Transactional annotations will not automatically take effect.

Exception caught

@Transactional
public void save() {
    try {
        userDao.insert();
    } catch (Exception e) {
        // 异常被吞掉后,事务拦截器无法按该异常自动回滚。
    }
}

Exception should be thrown again, or TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() should be called based on business needs.

Exception type mismatch

By default, exceptions under inspection do not trigger rollback. When rollback is needed, it should be clearly configured through rollbackFor.

use recommendations

  • Transactions are usually placed at the Service layer rather than the Controller layer.
  • A transaction covers only database operations that must be consistent.
  • Avoid long-term remote calls during transactions.
  • The query method can be set to read-only transactions.
  • Choose communication behavior based on business semantics, and do not configure all methods to REQUIRES_NEW.
  • When you convert exceptions, you should preserve the original exception information and ensure that the rollback rules are still in effect.

If you enjoyed this, leave a comment~

... Page views
© 2026 跨越星轨的客 @Hoshiumi
Powered by theme astro-koharu · Inspired by Shoka