Working with Transactions

1. Understanding Transaction Management (@Transactional)

Example: Transactional money transfer

@Service
public class TransferService {
  @Transactional
  public void transfer(Long from, Long to, BigDecimal amount) {
    accounts.debit(from, amount);
    accounts.credit(to, amount);
  } // commit on return; rollback on RuntimeException
}
Attribute Default
propagation REQUIRED
isolation DEFAULT (DB)
readOnly false
timeout -1 (none)
rollbackFor RuntimeException + Error

2. Configuring Transaction Propagation

Propagation Behavior
REQUIRED Join existing or start new
REQUIRES_NEW Suspend current, start new
SUPPORTS Join if exists, else non-tx
NOT_SUPPORTED Suspend if active
MANDATORY Throws if no active tx
NEVER Throws if tx active
NESTED Savepoint within outer tx

3. Setting Transaction Isolation Levels

Level Prevents
READ_UNCOMMITTED Nothing
READ_COMMITTED Dirty reads
REPEATABLE_READ + Non-repeatable reads
SERIALIZABLE + Phantom reads (slowest)

4. Configuring Transaction Timeout

Example: Transaction with timeout

@Transactional(timeout = 5) // seconds
public Report generate() { /* must finish in 5s */ }

5. Handling Read-Only Transactions

Effect Description
Hibernate flush mode Set to MANUAL (no dirty checking)
Connection Hint to use read replica (with custom routing)
Performance Skips snapshot creation, reduces overhead

6. Rolling Back Transactions

Example: Rollback rules and programmatic rollback

@Transactional(rollbackFor = Exception.class,
               noRollbackFor = NotFoundException.class)
public void process(Order o) throws BusinessException { /* … */ }

// Programmatic
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
Default Detail
Rolls back on Unchecked exceptions + Error
Commits on Checked exceptions (unless rollbackFor)

7. Using Programmatic Transactions (TransactionTemplate)

Example: Programmatic REQUIRES_NEW transaction

@Service
public class BatchService {
  private final TransactionTemplate tx;
  public BatchService(PlatformTransactionManager tm) {
    this.tx = new TransactionTemplate(tm);
    tx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
  }
  public void run() {
    tx.execute(status -> { /* work */ return null; });
  }
}

8. Understanding Transaction Boundaries

Pitfall Reason
Self-invocation Calling this.method() bypasses proxy → no tx
Private methods Not proxied → no tx
Final classes/methods CGLIB cannot proxy
Long tx Holds DB connection — limit duration
Warning: Apply @Transactional at the service entry, not on repository methods.

9. Handling Distributed Transactions (JTA)

Element Notes
JTA coordinator Atomikos, Narayana
Use case Multiple XA resources (DB + JMS)
Modern alt Saga / outbox pattern instead of XA

10. Using @Lock for Pessimistic Locking

Example: Pessimistic write lock query

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select a from Account a where a.id = :id")
Optional<Account> findForUpdate(@Param("id") Long id);
LockMode SQL
PESSIMISTIC_READ SELECT … FOR SHARE
PESSIMISTIC_WRITE SELECT … FOR UPDATE
OPTIMISTIC Version check on flush
OPTIMISTIC_FORCE_INCREMENT Bump version even if unchanged

11. Using @Version for Optimistic Locking

Example: Optimistic lock with @Version

@Entity
public class Product {
  @Id Long id;
  @Version Long version; // auto-incremented on update
}
Note: Update fails with OptimisticLockException if version differs.

12. Optimizing Transaction Performance

Tip Reason
Mark queries readOnly Skip dirty checking
Limit transaction scope Hold connections briefly
Use batch inserts spring.jpa.properties.hibernate.jdbc.batch_size=50
Avoid open-in-view Prevents view layer DB access
Connection pool sizing 2 × cores typically optimal