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

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.

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 Behavior | Meaning |
|---|---|
REQUIRED | default value. If there is a transaction, join it, and if there is no transaction, create a new transaction. |
SUPPORTS | joins if there is a transaction, and runs in a non-transactional manner if there is no transaction. |
MANDATORY | must have a transaction, otherwise an exception will be thrown. |
REQUIRES_NEW | always creates new transactions; if there are already transactions, outer transactions are suspended. |
NOT_SUPPORTED | runs non-transactional; if there is a transaction, the transaction is suspended. |
NEVER | must run non-transactional; if a transaction exists, an exception is thrown. |
NESTED | uses 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 Level | Meaning |
|---|---|
DEFAULT | uses the database default isolation level. |
READ_UNCOMMITTED | allows reading of uncommitted data, which may cause dirty reading, non-repeatable reading and phantom reading. |
READ_COMMITTED | can only read submitted data to avoid dirty reading. |
REPEATABLE_READ | Keep consistent when reading read rows repeatedly within the same transaction to avoid dirty and non-repeatable reads. |
SERIALIZABLE | transactions 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~