Tutorials Logic, IN info@tutorialslogic.com

Spring Transactions @Transactional, Propagation, Isolation: Tutorial, Examples, FAQs & Interview Tips

Spring Transactions @Transactional, Propagation, Isolation

Spring Transactions @Transactional, Propagation, Isolation is an important Spring topic because it appears in real projects, debugging sessions, and interviews. Learn the meaning first, then connect it to a small working example so the rule does not stay abstract.

For this page, focus on what problem Spring Transactions @Transactional, Propagation, Isolation solves, where developers usually make mistakes, and how to verify the result. The audit note for this lesson was: limited checklist/practice/mistake/FAQ notes .

A strong understanding of Spring Transactions @Transactional, Propagation, Isolation should include syntax, behavior, one realistic use case, one failure case, and one quick way to check your work with tools or output.

Spring Transactions @Transactional Propagation Isolation should be studied as a practical Spring lesson, not as a label. Start by naming the input, the rule that changes the input, and the result a learner should be able to predict after reading the page.

In the spring > spring-transactions page, the notes should connect the definition with a working scenario, a mistake that beginners actually make, and the exact check that proves the fix. That makes the topic useful for coding, debugging, and interview revision.

@Transactional Annotation

Spring's @Transactional annotation manages database transactions declaratively. When applied to a method or class, Spring wraps the execution in a transaction - committing on success and rolling back on unchecked exceptions (RuntimeException and Error) by default.

Add @EnableTransactionManagement to your configuration class (Spring Boot enables it automatically).

@Transactional with Propagation and Isolation

@Transactional with Propagation and Isolation
package com.example.service;

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 OrderService {

    private final OrderRepository orderRepository;
    private final InventoryService inventoryService;
    private final AuditService auditService;

    public OrderService(OrderRepository orderRepository,
                        InventoryService inventoryService,
                        AuditService auditService) {
        this.orderRepository  = orderRepository;
        this.inventoryService = inventoryService;
        this.auditService     = auditService;
    }

    // Default: REQUIRED propagation, DEFAULT isolation, rollback on RuntimeException
    @Transactional
    public Order placeOrder(Long userId, Long productId, int qty) {
        // All operations share the same transaction
        inventoryService.deductStock(productId, qty); // participates in this transaction
        Order order = orderRepository.save(new Order(userId, productId, qty));
        auditService.logOrder(order); // also in same transaction
        return order;
        // Commits here if no exception
    }

    // Read-only: hint to DB to optimize (no dirty checking, no flush)
    @Transactional(readOnly = true)
    public List<Order> getOrdersByUser(Long userId) {
        return orderRepository.findByUserId(userId);
    }

    // Rollback on checked exceptions too
    @Transactional(rollbackFor = Exception.class)
    public void processPayment(Long orderId) throws Exception {
        // Rolls back even if a checked Exception is thrown
    }

    // Never rollback on IllegalArgumentException
    @Transactional(noRollbackFor = IllegalArgumentException.class)
    public void updateOrder(Long orderId, String status) {
        // IllegalArgumentException won't trigger rollback
    }

    // SERIALIZABLE isolation: highest level, prevents phantom reads
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void criticalOperation() {
        // Fully isolated from other transactions
    }

    // REQUIRES_NEW: always starts a new transaction, suspends the current one
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logAuditAlways(String message) {
        // This runs in its own transaction - commits even if outer tx rolls back
        auditService.save(message);
    }
}

Propagation Types

Propagation Behavior
REQUIRED (default) Join existing transaction; create new one if none exists
REQUIRES_NEW Always create a new transaction; suspend existing one
SUPPORTS Join existing transaction; run non-transactionally if none
NOT_SUPPORTED Run non-transactionally; suspend existing transaction
MANDATORY Must run within an existing transaction; throw exception if none
NEVER Must NOT run within a transaction; throw exception if one exists
NESTED Run within a nested transaction (savepoint); rollback to savepoint on failure

Isolation Levels

Isolation Level Dirty Read Non-Repeatable Read Phantom Read
READ_UNCOMMITTED Possible Possible Possible
READ_COMMITTED Prevented Possible Possible
REPEATABLE_READ Prevented Prevented Possible
SERIALIZABLE Prevented Prevented Prevented

Programmatic Transactions with TransactionTemplate

Programmatic Transactions with TransactionTemplate
package com.example.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;

@Service
public class ProgrammaticTxService {

    private final TransactionTemplate transactionTemplate;
    private final OrderRepository orderRepository;

    public ProgrammaticTxService(PlatformTransactionManager txManager,
                                  OrderRepository orderRepository) {
        // TransactionTemplate wraps PlatformTransactionManager
        this.transactionTemplate = new TransactionTemplate(txManager);
        this.orderRepository     = orderRepository;
    }

    public Order createOrderProgrammatically(Order order) {
        // Configure transaction settings
        transactionTemplate.setIsolationLevel(
                TransactionTemplate.ISOLATION_READ_COMMITTED);
        transactionTemplate.setTimeout(30); // seconds

        // execute() returns a value; executeWithoutResult() for void
        return transactionTemplate.execute(status -> {
            try {
                Order saved = orderRepository.save(order);
                // ... more operations
                return saved;
            } catch (Exception e) {
                // Manually mark for rollback
                status.setRollbackOnly();
                throw new RuntimeException("Order creation failed", e);
            }
        });
    }
}

Common @Transactional Pitfalls

Common @Transactional Pitfalls
@Service
public class TransactionPitfalls {

    // PITFALL 1: Self-invocation bypasses the proxy - @Transactional has no effect
    public void outerMethod() {
        this.innerMethod(); // Calls directly, NOT through Spring proxy - NO transaction!
    }

    @Transactional
    public void innerMethod() {
        // This transaction is IGNORED when called from outerMethod() above
    }

    // FIX: Inject self or use a separate bean
    @Autowired
    private TransactionPitfalls self;

    public void outerMethodFixed() {
        self.innerMethod(); // Goes through proxy - transaction works!
    }

    // PITFALL 2: @Transactional on private methods - Spring proxy can't intercept
    @Transactional
    private void privateMethod() {
        // NO transaction - Spring AOP only works on public methods
    }

    // PITFALL 3: Catching RuntimeException prevents rollback
    @Transactional
    public void catchingException() {
        try {
            // ... DB operation
            throw new RuntimeException("error");
        } catch (RuntimeException e) {
            // Swallowing the exception - transaction COMMITS instead of rolling back!
            System.out.println("Caught: " + e.getMessage());
        }
    }

    // FIX: Re-throw or mark rollback manually
    @Transactional
    public void catchAndRethrow(TransactionStatus status) {
        try {
            throw new RuntimeException("error");
        } catch (RuntimeException e) {
            // Option 1: re-throw
            throw e;
            // Option 2: mark rollback only (if you need to handle the exception)
            // TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }
}

Spring Transactions @Transactional Propagation Isolation Java review example

Spring Transactions @Transactional Propagation Isolation Java review example
class SpringTransactionsTransactionalPropagationIsolationReview {
    public static void main(String[] args) {
        String state = "ready";
        System.out.println("Spring Transactions @Transactional Propagation Isolation: " + state);
    }
}

Spring Transactions @Transactional Propagation Isolation guard example

Spring Transactions @Transactional Propagation Isolation guard example
String value = null;
if (value == null) {
    System.out.println("Spring Transactions @Transactional Propagation Isolation: handle the missing value before continuing");
}
Key Takeaways
  • Explain the purpose of Spring Transactions @Transactional, Propagation, Isolation before memorizing syntax.
  • Run or trace one small Spring example and confirm the output.
  • Test one normal case, one edge case, and one mistake case for Spring Transactions @Transactional, Propagation, Isolation.
  • Write the rule in your own words after checking the example.
  • Connect Spring Transactions @Transactional, Propagation, Isolation to a real project scenario instead of treating it as an isolated definition.
Common Mistakes to Avoid
WRONG Memorizing Spring Transactions @Transactional Propagation Isolation without the situation where it is useful.
RIGHT Connect Spring Transactions @Transactional Propagation Isolation to a concrete Spring task.
Purpose makes syntax easier to recall.
WRONG Testing Spring Transactions @Transactional Propagation Isolation only with the perfect input.
RIGHT Include empty, missing, duplicate, incompatible, or failed cases when relevant.
Real bugs usually appear outside the perfect path.
WRONG Changing code before reading the visible symptom or error message.
RIGHT Inspect the output, state, configuration, or stack trace connected to Spring Transactions @Transactional Propagation Isolation.
Evidence keeps debugging focused.
WRONG Memorizing Spring Transactions @Transactional Propagation Isolation without the situation where it is useful.
RIGHT Connect Spring Transactions @Transactional Propagation Isolation to a concrete Spring task.
Purpose makes syntax easier to recall.

Practice Tasks

  • Modify the example so it handles a different input or condition.
  • Write one mistake related to Spring Transactions @Transactional, Propagation, Isolation, then fix it and explain the fix.
  • Summarize when to use Spring Transactions @Transactional, Propagation, Isolation and when another approach is better.
  • Write a small example that uses Spring Transactions @Transactional Propagation Isolation in a realistic Spring scenario.
  • Change one important value in the Spring Transactions @Transactional Propagation Isolation example and predict the result first.

Frequently Asked Questions

The common mistake is memorizing syntax without understanding when the behavior changes or fails.

Remember the problem it solves in Spring, then attach the syntax or steps to that problem.

You can predict the result of a small example, explain a failure case, and choose it over a nearby alternative for a clear reason.

They often copy the syntax but skip the state, input, dependency, selector, route, type, or configuration that controls the behavior.

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.