5.5 Transaction Management
← Back to Chapter 5: Spring Data JPA, Hibernate & Database
Transactions preserve database consistency under the ACID guarantees (Atomicity, Consistency, Isolation, Durability). Spring provides declarative transaction management via @Transactional.
1. Transaction Propagation Behaviors
When a transactional method calls another transactional method, Propagation controls whether to join the existing transaction or spawn a new one:
| Propagation | Behavior |
|---|---|
REQUIRED (default) | Joins existing transaction if present. Creates a new one if none exists. |
REQUIRES_NEW | Suspends existing transaction and creates an entirely independent transaction. Committed independently. |
SUPPORTS | Executes within active transaction if present; executes non-transactionally if none exists. |
NOT_SUPPORTED | Executes non-transactionally; suspends active transaction if present. |
MANDATORY | Requires an existing active transaction. Throws TransactionRequiredException if none exists. |
NEVER | Throws exception if an active transaction exists. |
Practical Use Case for REQUIRES_NEW:
Audit logging! If a business order transaction fails and rolls back, the audit log recording the failed attempt must still be committed:
@Service
public class AuditService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void recordSecurityEvent(String event) {
auditRepository.save(new SecurityAudit(event)); // Commits even if caller rolls back!
}
}
2. Transaction Isolation Levels
Isolation dictates how concurrent transactions interact and prevent database phenomena:
| Isolation Level | Dirty Reads? | Non-Repeatable Reads? | Phantom Reads? |
|---|---|---|---|
READ_UNCOMMITTED | Yes | Yes | Yes |
READ_COMMITTED (Postgres default) | No | Yes | Yes |
REPEATABLE_READ (MySQL default) | No | No | Yes |
SERIALIZABLE (Strict locking) | No | No | No |
3. Rollback Rules & Read-Only Optimization
Default Rollback Policy:
By default, Spring rolls back only for unchecked exceptions (RuntimeException and Error). It does NOT roll back for checked exceptions (Exception) unless configured:
// Roll back for all exceptions:
@Transactional(rollbackFor = Exception.class)
public void transferMoney(UUID from, UUID to, BigDecimal amount) throws InsufficientFundsException {
accountService.debit(from, amount);
accountService.credit(to, amount);
}
Read-Only Optimization:
@Transactional(readOnly = true)
public UserDTO findUser(Long id) { ... }
When readOnly = true:
- Hibernate disables dirty checking on loaded entities, saving CPU cycles and memory snapshots.
- Directs queries to database read-replicas in multi-node clusters.