MindIQ Academy

05 - Spring Transactions Notes

A beginner-to-advanced guide to Spring transaction management, @Transactional, propagation, isolation, rollback rules, and the transaction manager for Spring Professional Certification candidates. Covers Spring Framework 6 and Spring Boot 3 concepts.


Table of Contents

  1. @Transactional
  2. Propagation
  3. Isolation
  4. Rollback Rules
  5. Transaction Manager
  6. Nested Transactions
  7. Read Only Transactions
  8. Common Pitfalls
  9. Interview Questions
  10. Cheat Sheet

1. @Transactional

@Transactional declares that a method or class should run inside a Spring-managed transaction.

@Service
public class PaymentService {

    @Transactional
    public void pay(Long orderId) {
        // database operations are executed in one transaction
    }
}

When placed on a class, it applies to all public methods unless overridden at method level.

@Service
@Transactional(readOnly = true)
public class OrderQueryService {

    public Order findById(Long id) {
        return repository.findById(id).orElseThrow();
    }

    @Transactional(readOnly = false)
    public Order create(Order order) {
        return repository.save(order);
    }
}

Important Rules

RuleExplanation
Works through Spring proxiesCalls must go through the Spring proxy for transaction advice to apply.
Public methods are the usual targetWith proxy-based AOP, public methods are the common and recommended use.
Self-invocation does not trigger transactionsA method inside the same class calling another @Transactional method bypasses the proxy.
Runtime exceptions roll back by defaultChecked exceptions do not roll back unless configured.
Keep transactions shortAvoid slow network calls or long computations inside transactions.

Common Attributes

@Transactional(
    propagation = Propagation.REQUIRED,
    isolation = Isolation.READ_COMMITTED,
    readOnly = false,
    timeout = 30,
    rollbackFor = Exception.class
)
AttributePurpose
propagationDefines how the method participates in an existing transaction.
isolationDefines how visible concurrent changes are.
readOnlyOptimizes the transaction for read operations.
timeoutMaximum time before the transaction times out.
rollbackForExceptions that should trigger rollback.
noRollbackForExceptions that should not trigger rollback.

2. Propagation

Propagation controls what happens when a transactional method is called while another transaction may already exist.

REQUIRED

Uses the existing transaction if one exists; otherwise creates a new one.

@Transactional(propagation = Propagation.REQUIRED)
public void placeOrder() {
}

This is the default and most commonly used propagation type.

REQUIRES_NEW

Always starts a new transaction. If another transaction exists, it is suspended.

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveAuditLog() {
}

Useful when an operation must commit independently, such as audit logging.

SUPPORTS

Uses an existing transaction if one exists; otherwise runs without a transaction.

@Transactional(propagation = Propagation.SUPPORTS)
public List<Order> findOrders() {
    return repository.findAll();
}

Useful for read operations that can work with or without a transaction.

MANDATORY

Requires an existing transaction. Throws an exception if none exists.

@Transactional(propagation = Propagation.MANDATORY)
public void updateInventory() {
}

Useful when a method must only be called as part of a larger transactional workflow.

NOT_SUPPORTED

Suspends an existing transaction and runs without one.

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void generateReport() {
}

Useful for long-running non-transactional work.

NEVER

Runs only if no transaction exists. Throws an exception if a transaction is active.

@Transactional(propagation = Propagation.NEVER)
public void nonTransactionalOperation() {
}

NESTED

Runs inside a nested transaction using a savepoint if a transaction exists. If no transaction exists, behaves like REQUIRED.

@Transactional(propagation = Propagation.NESTED)
public void applyDiscount() {
}

Useful when part of a larger transaction can roll back independently to a savepoint.

Propagation Summary

PropagationExisting TransactionNo Existing Transaction
REQUIREDJoin existing transactionCreate new transaction
REQUIRES_NEWSuspend existing and create newCreate new transaction
SUPPORTSJoin existing transactionRun non-transactionally
MANDATORYJoin existing transactionThrow exception
NOT_SUPPORTEDSuspend existing transactionRun non-transactionally
NEVERThrow exceptionRun non-transactionally
NESTEDCreate savepoint inside existing transactionCreate new transaction

3. Isolation

Isolation controls how one transaction is affected by concurrent transactions.

Concurrency Problems

ProblemMeaning
Dirty readReading uncommitted data from another transaction.
Non-repeatable readReading the same row twice and seeing different committed values.
Phantom readRe-running a query and seeing new or removed rows due to another committed transaction.
Lost updateTwo transactions update the same data and one update overwrites the other.

Isolation Levels

Isolation LevelDirty ReadNon-Repeatable ReadPhantom ReadNotes
READ_UNCOMMITTEDPossiblePossiblePossibleLowest isolation; rarely used.
READ_COMMITTEDPreventedPossiblePossibleCommon default in many databases.
REPEATABLE_READPreventedPreventedPossibleCommon default in MySQL InnoDB.
SERIALIZABLEPreventedPreventedPreventedHighest isolation; safest but slowest.

Spring Isolation Values

@Transactional(isolation = Isolation.READ_COMMITTED)
public void updateAccount() {
}
ValueDescription
DEFAULTUses the default isolation level of the database.
READ_UNCOMMITTEDAllows dirty reads.
READ_COMMITTEDPrevents dirty reads.
REPEATABLE_READPrevents dirty and non-repeatable reads.
SERIALIZABLEFully serializes transactions.

4. Rollback Rules

By default, Spring rolls back transactions for unchecked exceptions.

Exception TypeDefault Behavior
RuntimeExceptionRollback
ErrorRollback
Checked exceptionCommit unless configured

Rollback for Checked Exceptions

@Transactional(rollbackFor = Exception.class)
public void importData() throws Exception {
    throw new Exception("Import failed");
}

Prevent Rollback for Specific Exceptions

@Transactional(noRollbackFor = BusinessException.class)
public void process() {
    throw new BusinessException("Business validation failed");
}

Programmatic Rollback

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

Use programmatic rollback sparingly. Prefer exception-based rollback rules because they are clearer and easier to test.

Rollback-Only Marker

If an inner transactional operation marks a transaction as rollback-only, the outer transaction cannot commit successfully. Spring may throw UnexpectedRollbackException when the outer method tries to commit.

@Transactional
public void outer() {
    inner();
}

@Transactional
public void inner() {
    throw new RuntimeException("Failure");
}

If inner() participates in the same transaction and the exception causes rollback, the whole transaction is affected.

5. Transaction Manager

A transaction manager coordinates transaction boundaries: begin, commit, rollback, suspend, and resume.

Spring uses the PlatformTransactionManager abstraction.

public interface PlatformTransactionManager {
    TransactionStatus getTransaction(TransactionDefinition definition);
    void commit(TransactionStatus status);
    void rollback(TransactionStatus status);
}

Common Transaction Managers

Transaction ManagerUsed For
DataSourceTransactionManagerPlain JDBC and MyBatis with a single DataSource.
JpaTransactionManagerJPA/Hibernate.
JtaTransactionManagerDistributed transactions across multiple resources.
R2dbcTransactionManagerReactive transactions with R2DBC.

Spring Boot Auto-Configuration

Spring Boot automatically configures a transaction manager when it finds transaction-capable infrastructure.

Examples:

Dependency/BeanAuto-Configured Manager
JDBC DataSourceDataSourceTransactionManager
JPA EntityManagerFactoryJpaTransactionManager
R2DBC ConnectionFactoryR2dbcTransactionManager

If multiple transaction managers exist, specify which one to use.

@Transactional(transactionManager = "ordersTransactionManager")
public void updateOrder() {
}

Declarative vs Programmatic Transactions

Declarative transaction:

@Transactional
public void createUser() {
}

Programmatic transaction:

transactionTemplate.execute(status -> {
    repository.save(user);
    return null;
});

Prefer declarative transactions for most business services. Use TransactionTemplate when transaction boundaries must be controlled inside a method.

6. Nested Transactions

Nested transactions use savepoints inside an existing transaction.

@Transactional
public void checkout() {
    reserveInventory();
    applyCoupon();
    chargePayment();
}

@Transactional(propagation = Propagation.NESTED)
public void applyCoupon() {
}

If applyCoupon() fails, Spring can roll back to the savepoint without necessarily rolling back the entire outer transaction.

NESTED vs REQUIRES_NEW

FeatureNESTEDREQUIRES_NEW
Transaction typeSavepoint inside existing transactionCompletely separate transaction
Outer transactionContinues after savepoint rollbackSuspended while inner transaction runs
Commit behaviorFinal commit depends on outer transactionInner transaction commits independently
Resource usageUsually same database connectionUsually needs another connection
SupportRequires savepoint supportWidely supported

Limitations

LimitationExplanation
Requires savepoint supportThe underlying transaction manager and database must support savepoints.
Not the same as independent commitA nested transaction does not commit independently of the outer transaction.
Proxy rules still applySelf-invocation can prevent NESTED from being applied.

7. Read Only Transactions

readOnly = true tells Spring and the persistence provider that the transaction is intended only for reads.

@Transactional(readOnly = true)
public List<Product> findProducts() {
    return productRepository.findAll();
}

Benefits

BenefitExplanation
Optimization hintThe database or ORM may optimize for reads.
Hibernate optimizationHibernate may skip dirty checking or adjust flush behavior.
Clear intentShows that the method should not modify data.

Important Notes

NoteExplanation
Not always enforcedSome databases treat read-only as a hint.
Writes may still happen in some casesDo not rely only on readOnly = true for security or validation.
Best for query servicesUse it on read-only service methods or classes.
@Service
@Transactional(readOnly = true)
public class ProductQueryService {

    public ProductDetails getDetails(Long id) {
        return productRepository.findDetails(id);
    }
}

8. Common Pitfalls

PitfallWhy It HappensFix
@Transactional not working on private methodProxy cannot intercept private methodPut it on a public service method
Self-invocation bypasses transactionMethod call does not go through proxyMove method to another bean or call through proxy
Checked exception does not roll backSpring rolls back unchecked exceptions by defaultAdd rollbackFor
Long transaction causes locksTransaction holds database resources too longKeep transaction boundaries small
LazyInitializationExceptionEntity lazy field accessed outside transaction/sessionFetch required data inside transaction or use DTO queries
Unexpected rollbackInner operation marks shared transaction rollback-onlyUse proper propagation or exception handling

9. Interview Questions

1. What does @Transactional do?

It defines a transactional boundary around a method or class. Spring opens a transaction before method execution and commits it if the method completes successfully. If a rollback-triggering exception occurs, Spring rolls back the transaction.

2. Why does @Transactional sometimes not work?

Common reasons include self-invocation, using it on private methods, calling a method on an object not managed by Spring, missing transaction manager configuration, or catching exceptions without rethrowing them.

3. What is the default propagation level?

The default propagation is REQUIRED. It joins an existing transaction or creates a new one if none exists.

4. What is the difference between REQUIRED and REQUIRES_NEW?

REQUIRED participates in the current transaction. REQUIRES_NEW suspends the current transaction and starts a separate new transaction that can commit or roll back independently.

5. What is the difference between NESTED and REQUIRES_NEW?

NESTED uses a savepoint within the existing transaction. REQUIRES_NEW creates a completely separate transaction. A nested transaction depends on the outer transaction's final commit, while a REQUIRES_NEW transaction commits independently.

6. Which exceptions cause rollback by default?

Spring rolls back by default for RuntimeException and Error. Checked exceptions do not cause rollback unless configured with rollbackFor.

7. How do you roll back for a checked exception?

@Transactional(rollbackFor = IOException.class)
public void upload() throws IOException {
}

8. What is transaction isolation?

Isolation defines how changes made by one transaction are visible to other concurrent transactions. Higher isolation improves consistency but can reduce concurrency.

9. What is a dirty read?

A dirty read happens when one transaction reads uncommitted data from another transaction. If the other transaction rolls back, the first transaction has read invalid data.

10. What is a phantom read?

A phantom read happens when a transaction runs the same query multiple times and sees a different set of rows because another transaction inserted or deleted matching rows.

11. What does readOnly = true do?

It marks the transaction as read-only. It can help the database or ORM optimize execution and communicates that the method should not perform writes.

12. Can @Transactional be used on interfaces?

It can be used on interfaces, but placing it on concrete service classes or methods is usually clearer and more reliable, especially when proxy and inheritance behavior matters.

13. What is UnexpectedRollbackException?

It occurs when a transaction is marked rollback-only but the outer code still attempts to commit. This often happens when an inner transactional operation fails inside a shared transaction.

14. What is the role of PlatformTransactionManager?

It abstracts transaction operations such as beginning, committing, rolling back, suspending, and resuming transactions.

15. When would you use TransactionTemplate?

Use TransactionTemplate when transaction boundaries need to be controlled programmatically inside a method or when different parts of the same method need different transactional behavior.

10. Cheat Sheet

@Transactional Defaults

SettingDefault
PropagationREQUIRED
IsolationDEFAULT
RollbackRuntimeException and Error
Read-onlyfalse
TimeoutDatabase or transaction manager default

Propagation Quick Reference

NeedUse
Join current transaction or create oneREQUIRED
Always create independent transactionREQUIRES_NEW
Run with transaction only if one existsSUPPORTS
Fail if no transaction existsMANDATORY
Always run without transactionNOT_SUPPORTED
Fail if a transaction existsNEVER
Roll back part of a transaction to savepointNESTED

Isolation Quick Reference

NeedUse
Use database defaultDEFAULT
Maximum performance, weak consistencyREAD_UNCOMMITTED
Prevent dirty readsREAD_COMMITTED
Prevent dirty and non-repeatable readsREPEATABLE_READ
Strongest consistencySERIALIZABLE

Rollback Quick Reference

@Transactional

Rolls back on unchecked exceptions.

@Transactional(rollbackFor = Exception.class)

Rolls back on checked and unchecked exceptions.

@Transactional(noRollbackFor = BusinessException.class)

Does not roll back for BusinessException.

Best Practices

PracticeReason
Put transactions on service methodsKeeps business operations atomic.
Keep transactions shortReduces locks and contention.
Avoid external API calls inside transactionsPrevents long-held database resources.
Use readOnly = true for queriesImproves clarity and may improve performance.
Be explicit for checked exceptionsDefault rollback does not include them.
Choose propagation intentionallyPrevents accidental shared rollback behavior.
Use DTO queries for read-heavy screensAvoids lazy loading issues and unnecessary entity tracking.