Skip to main content

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:

PropagationBehavior
REQUIRED (default)Joins existing transaction if present. Creates a new one if none exists.
REQUIRES_NEWSuspends existing transaction and creates an entirely independent transaction. Committed independently.
SUPPORTSExecutes within active transaction if present; executes non-transactionally if none exists.
NOT_SUPPORTEDExecutes non-transactionally; suspends active transaction if present.
MANDATORYRequires an existing active transaction. Throws TransactionRequiredException if none exists.
NEVERThrows 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 LevelDirty Reads?Non-Repeatable Reads?Phantom Reads?
READ_UNCOMMITTEDYesYesYes
READ_COMMITTED (Postgres default)NoYesYes
REPEATABLE_READ (MySQL default)NoNoYes
SERIALIZABLE (Strict locking)NoNoNo

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:

  1. Hibernate disables dirty checking on loaded entities, saving CPU cycles and memory snapshots.
  2. Directs queries to database read-replicas in multi-node clusters.