Home/Learn/Hibernate & JPA/Transactions in JPA

Transactions in JPA

Intermediate
Transactions

JPA transactions are demarcated by EntityTransaction or (in Spring) @Transactional; a transaction spans the persistence context lifecycle for a unit of work.

Overview

JPA requires an active transaction for any write operation (persist, merge, remove). Outside a transaction, entities are detached and changes are ignored. In Spring, @Transactional is the declarative mechanism: Spring AOP wraps the method in begin/commit/rollback calls, and the EntityManager (persistence context) lives for the duration of the transaction. Read operations do not strictly require a transaction but should use one to avoid multiple underlying JDBC connections for lazy loading. Understanding flush modes is important: the default FlushModeType.AUTO flushes the persistence context before any query that might return stale data, ensuring consistency within the transaction. The persistence context tracks entity state (new, managed, detached, removed) and synchronises it to the DB on flush.

Entity lifecycle and @Transactional boundaries

Entity state transitions (new → managed → detached → removed) are tied to the persistence context. An entity becomes detached when the transaction ends. Modifying a detached entity throws an exception unless it is re-merged.

Java — entity lifecycle within @Transactional
@Service
@Transactional  // class-level: all public methods participate in a tx
public class OrderService {

    @PersistenceContext
    private EntityManager em;

    // New entity: em.persist() moves it to managed state
    public Order createOrder(OrderRequest req) {
        Order order = new Order(req.getCustomerId(), req.getItems());
        em.persist(order);          // state: NEW → MANAGED
        return order;               // entity is still managed here
    }                               // tx commits → flush → INSERT → entity DETACHED

    // Fetching within tx: entity is managed (dirty-checking active)
    @Transactional
    public void updateStatus(Long id, String status) {
        Order order = em.find(Order.class, id); // state: MANAGED
        order.setStatus(status);                // dirty-checked, no explicit save
    }                               // tx commits → flush → UPDATE auto-issued

    // Detached entity — requires merge
    @Transactional
    public Order updateDetached(Order detachedOrder) {
        return em.merge(detachedOrder); // DETACHED → MANAGED (SELECT + UPDATE)
    }

    // Remove
    @Transactional
    public void cancel(Long id) {
        Order order = em.find(Order.class, id);
        em.remove(order);           // state: MANAGED → REMOVED
    }                               // tx commits → DELETE
}

FlushMode and the persistence context flush

Hibernate flushes automatically before queries (AUTO) or only on transaction commit (COMMIT). Understanding AUTO prevents surprising SELECT → UPDATE → SELECT sequences.

Java — FlushMode AUTO vs COMMIT vs readOnly
@Transactional
public void processOrder(Long id) {
    Order order = em.find(Order.class, id);
    order.setStatus("PROCESSING");
    // FlushModeType.AUTO: Hibernate detects the pending dirty change
    // and flushes it BEFORE the JPQL query below to avoid stale results
    List<Order> processingOrders = em.createQuery(
            "SELECT o FROM Order o WHERE o.status = 'PROCESSING'", Order.class)
            .getResultList();
    // order IS included in processingOrders because AUTO flushed it first
}

// Override to COMMIT mode for read-heavy operations (fewer flushes)
@Transactional
public List<Order> readOrders() {
    em.setFlushMode(FlushModeType.COMMIT); // no auto-flush before queries
    return em.createQuery("SELECT o FROM Order o", Order.class)
             .getResultList();
}

// Spring Data JPA read-only hint — optimises flush and snapshot behaviour
@Transactional(readOnly = true)
public List<Order> findAll() {
    // Hibernate skips dirty-checking snapshots for managed entities
    return orderRepository.findAll();
}

Transaction rollback: checked vs unchecked exceptions

@Transactional rolls back on RuntimeException by default. Checked exceptions do NOT trigger rollback unless explicitly configured. This is a common bug source.

Java — rollback rules for checked exceptions
// DEFAULT: only RuntimeException triggers rollback
@Transactional
public void processPayment(Long orderId) throws PaymentException {
    Order order = orderRepository.findById(orderId).orElseThrow();
    paymentGateway.charge(order.getAmount());  // throws checked PaymentException
    order.setStatus("PAID");
    // BUG: if PaymentException is thrown, order.setStatus("PAID") is flushed
    // because checked exceptions do NOT rollback by default!
}

// FIX 1: rollbackFor
@Transactional(rollbackFor = PaymentException.class)
public void processPayment(Long orderId) throws PaymentException { ... }

// FIX 2: wrap in RuntimeException
@Transactional
public void processPayment(Long orderId) {
    try {
        doProcessPayment(orderId);
    } catch (PaymentException e) {
        throw new PaymentProcessingException("Payment failed", e); // RuntimeException
    }
}

// Prevent rollback for specific exceptions (e.g. business validation)
@Transactional(noRollbackFor = ValidationException.class)
public void validate(Long id) throws ValidationException { ... }

Key Points to Remember

  • 1JPA requires an active transaction for writes; Spring @Transactional provides AOP-based begin/commit/rollback.
  • 2Entities are managed within a transaction and automatically flushed to DB before relevant queries (AUTO mode).
  • 3@Transactional(readOnly = true) skips dirty-checking snapshots — use it for all read-only service methods.
  • 4Only RuntimeException triggers rollback by default — always specify rollbackFor for checked exceptions on write methods.
  • 5Merging a detached entity issues a SELECT + UPDATE; fetch fresh from DB inside the transaction when possible.
  • 6Open Session in View pattern is an anti-pattern in production — it extends the persistence context into the view layer, causing N+1 and resource leaks.

Interview Questions

Sign in to ask Aria
1

What is the JPA persistence context and how long does it live in a Spring @Transactional method?

EasyAmazon
2

Why does @Transactional not rollback on a checked exception by default and how do you fix it?

MediumNetflix
3

What is FlushModeType.AUTO and when does Hibernate flush the persistence context?

MediumZalando
4

Explain the difference between em.persist(), em.merge(), and em.save() in Spring Data.

MediumShopify
5

What happens if you modify an entity after calling em.remove() before the transaction commits?

HardGoogle

Ask Aria about Transactions in JPA

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…