1. Spring Transaction Attributes Overview
Building upon foundational transaction control concepts, Spring provides the @Transactional annotation for declarative transaction management. Understanding these attributes is essential for effective transaction configuration.
@Transactional(isolation=Isolation.DEFAULT, rollbackFor=ArithmeticException.class, timeout=-1, readOnly=false, propagation=Propagation.REQUIRED)
Key attributes include:
- propagation: Defines the transactional boundary between the calling method and the called method
- isolation: Controls database safety during concurrent access
- timeout: Maximum transaction duration. If a transaction remains uncommitted or unrolled back beyond this threshold, the system automatically rolls it back. Unit is seconds. A value of -1 means no timeout, deferring to the underlying database configuration.
- readOnly: Read-only transactions optimize query operasions since they do not modify any data
- rollbackFor and noRollbackFor: Specify whether to rollback or commit based on exception type
Isolation Level Configurations:
1. DEFAULT
Uses the default isolation level configured by the underlying PlatformTransactionManager, typically corresponding to one of the four JDBC isolation levels.
2. READ_UNCOMMITTED
The lowest isolation level, allowing one transaction to see uncommitted data from another. This level can cause dirty reads, non-repeatable reads, and phantom reads.
3. READ_COMMITTED
Ensures that modified data is only visible to other transactions after commit. This prevents dirty reads but may still allow non-repeatable reads and phantom reads.
4. REPEATABLE_READ
Prevents dirty reads and non-repeatable reads by ensuring a transaction cannot read uncommitted data from another transaction and cannot modify data that has been read by other transactions. Phantom reads may still occur.
5. SERIALIZABLE
The most reliable but most expensive isolation level. Transactions execute sequentially, preventing dirty reads, non-repeatable reads, and phantom reads.
Propagation Behavior Configurations:
| Propagation | Description |
|---|---|
| REQUIRED | Method executes within an existing transaction. If none exists, creates a new transaction. Currently the most commonly used propagation behavior. |
| NOT_SUPPORTED | Method does not require transaction support. If called within a transaction, that transaction suspends and resumes after the method completes. |
| REQUIRES_NEW | Method always creates a new transaction. If already in a transaction, the existing transaction suspends until this new transaction completes. |
| MANDATORY | Method must execute within an existing transaction. Throws an exception if called without an active transaction. |
| SUPPORTS | Method participates in an existing transaction if one is present, otherwise executes without transaction context. |
| NEVER | Method must not execute within any transaction. Throws an exception if called within a transaction context. |
| NESTED | Executes within a nested transaction if an active transaction exists, otherwise behaves like REQUIRED. Uses savepoints for rollback capability. Only works with DataSourceTransactionManager. |
2. Transaction Propagation Behavior Example
Consider a scenario where a service class coordinates multiple DAO operations with transactional requirements. The challenge arises when you need independent transaction control across different DAO components within a single service method.
For instance, in a service method called processData, if both logDao and personDao are configured with the same propagation level of REQUIRED, both operations will share the same transaction context. When an exception occurs in processData, both DAO operations will be rolled back together.
To achieve independent rollback behavior—where personDao rolls back on failure but logDao commits successfully—you can configure logDao with REQUIERS_NEW propagation. When processData executes, transaction A starts. Upon reaching logDao, transaction A suspends and transaction B initiates for logDao. Once logDao completes, transaction B commits and transaction A resumes. This ensures logDao operations remain unaffected even when personDao operations fail.
import javax.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional;
@Service public class UserManagementService {
@Resource
private UserDao userDao;
@Resource
private AuditLogDao auditLogDao;
/*
* @Transactional placement determines scope:
* 1. Method level: Only that method uses Spring declarative transaction
* 2. Class level: All methods in the class use declarative transaction
* 3. Interface level: All implementing methods use declarative transaction
*/
@Transactional(
readOnly = false,
timeout = -1,
isolation = Isolation.DEFAULT,
propagation = Propagation.REQUIRED
)
public void processData(User user) {
auditLogDao.record("Processing user operation initiated");
int errorCondition = 1 / 0;
userDao.create(user);
}
}
</div><div>```
package com.example.spring.transaction.demo;
import javax.annotation.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Repository
public class AuditLogDao {
@Resource
private JdbcTemplate jdbcTemplate;
@Transactional(
propagation = Propagation.REQUIRES_NEW
)
public void record(String message) {
String sql = "INSERT INTO audit_logs(content) VALUES(?)";
jdbcTemplate.update(sql, message);
}
}
Transaction grouping is a widely adopted pattern in enterprise applications for managing long business workflows. This approach segments complex operations into independent modules with isolated transaction contexts, enabling partial rollback scenarios where some operations commit while others fail.
Consider a workflow with sequential steps A→B→C→D. Various rollback strategies become possible: only step C rolls back on error while A, B, and D commit; only A and B roll back when A or B fails; only D rolls back when D fails.
Achieving these scenarios requires combining REQUIRED and REQUIRES_NEW propagation behaviors strategically. By wrapping the entire chain ABCD in an execute method with REQUIRED propagation, assigning REQUIRES_NEW to steps A and B to share one transaction context, keeping step C in the parent transaction, and assigning REQUIRES_NEW to step D, you can implement the desired transaction isolation behavior.
Implementation Structure:
execute() {
[REQUIRED]
methodAB();
methodC();
methodD();
}
methodAB() {
[REQUIRES_NEW]
stepA();
stepB();
}
This architecture enables fine-grained control over transaction boundaries in complex business scenarios.