skip to content

What does @Transactional(propagation = REQUIRES_NEW) do, and how does it interact with the JDBC connection of an already-running transaction?

level: juniorimportance: must knowfreq 55%

answer

  1. REQUIRES_NEW = always new independent tx
  2. Outer suspended, NOT released
  3. Two connections, one thread
  4. Not a savepoint (that's NESTED)
  5. Self-invocation bypasses proxy

basics

~20 s

REQUIRES_NEW starts a brand-new, independent transaction. If one is already running, Spring suspends it (keeps it open) and opens a second database connection for the new one, so the thread holds two connections at once.

solid answer

~40 s

Propagation.REQUIRES_NEW tells Spring to always run the method in its own new transaction. If the caller is already inside a transaction (typically REQUIRED), Spring suspends that outer transaction rather than joining it, then begins a completely independent inner transaction with its own physical JDBC Connection borrowed from the pool. The key detail: suspending does not release the outer connection. The outer transaction is still open and its Connection stays bound to the thread, just paused. So while the inner method runs, one thread holds two connections from the same DataSource simultaneously. The inner transaction commits or rolls back independently of the outer; then the outer resumes and finishes. This double-hold is exactly what makes nested REQUIRES_NEW dangerous under load.

code

java · 28 lines
java
@Service
public class OrderService {

    private final AuditService auditService;

    public OrderService(AuditService auditService) {
        this.auditService = auditService;
    }

    // Outer: default propagation REQUIRED -> holds Connection #1
    @Transactional
    public void placeOrder(Order order) {
        orderRepo.save(order);
        // Cross-bean call so the proxy applies REQUIRES_NEW:
        auditService.record("ORDER_PLACED", order.getId()); // borrows Connection #2
        // ...more work on Connection #1 after audit commits
    }
}

@Service
class AuditService {
    // Inner: independent tx -> Spring SUSPENDS the outer (keeps its Connection)
    // and opens a SECOND Connection from the same pool.
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void record(String type, Long id) {
        auditRepo.save(new AuditEntry(type, id)); // commits on its own
    }
}

go deeper

for a junior

Must know REQUIRES_NEW = new independent transaction and that it means a second connection.

for a middle

Should articulate suspend semantics: outer connection held, not released, hence two connections per thread.

for a senior

Contrasts REQUIRES_NEW vs NESTED vs REQUIRED and knows the self-invocation proxy pitfall.

for a principal

Frames the two-connection hold as a systemic capacity constraint feeding into pool sizing and deadlock risk.

**Transaction propagation** is Spring's rule for what happens when a `@Transactional` method is called while a transaction may or may not already be active on the current thread. It is set via `@Transactional(propagation = Propagation.XXX)`. **REQUIRED** (the default): join the existing transaction if there is one, otherwise start a new one. One logical transaction maps to one physical `Connection`. **REQUIRES_NEW**: *always* run in a brand-new, independent transaction. If a transaction is already active, Spring **suspends** it and starts a fresh one. ### What 'suspend' actually means When Spring's `AbstractPlatformTransactionManager` (e.g. `DataSourceTransactionManager` for plain JDBC/JPA-over-JDBC) suspends the outer transaction, it detaches the outer transaction's resources (the bound `Connection`) from the thread and stashes them in a `SuspendedResourcesHolder`. Crucially, **the outer Connection is NOT returned to the pool** — the outer transaction is still open (it has uncommitted work and must resume later), so its Connection is held aside. Spring then asks the `DataSource` for a **second** Connection for the inner transaction and binds that to the thread via `TransactionSynchronizationManager`. So during the inner method the thread simultaneously owns: 1. the suspended outer Connection (paused, still checked out of the pool), and 2. the active inner Connection. Two physical connections, one thread, one pool. ### Independence of the inner transaction The inner transaction commits or rolls back on its own. A rollback of the inner transaction does **not** roll back the outer, and vice versa — that isolation is usually the reason people reach for REQUIRES_NEW (e.g. writing an audit/outbox row that must persist even if the main work fails). After the inner finishes, Spring resumes the outer by rebinding the stashed Connection to the thread. ### Why juniors get surprised Many assume REQUIRES_NEW just 'reuses the same connection with a savepoint' — that is actually **NESTED** propagation (JDBC savepoints, single connection), a different thing. REQUIRES_NEW is a genuinely separate physical transaction, hence a separate connection. ### The lurking danger Because the pattern holds two connections per thread, a connection pool sized N can only support N/2 threads doing this concurrently. Beyond that, threads block waiting for a second connection that will never free up — a deadlock. That is the subject of the harder questions in this leaf. ### Self-invocation gotcha One more: `@Transactional` works through a Spring AOP proxy. If an outer method calls an inner REQUIRES_NEW method **on `this`** (same bean, direct call), the proxy is bypassed and the propagation is silently ignored — no new transaction, no suspend. You must call across a bean boundary (inject the other bean, or self-inject the proxy) for REQUIRES_NEW to take effect.

  • How is REQUIRES_NEW different from NESTED propagation?
    NESTED runs inside the SAME physical transaction/connection using a JDBC savepoint; rolling back the nested part only rolls back to the savepoint and the outer can continue. REQUIRES_NEW is a fully separate physical transaction with its own connection that commits independently.
  • If the inner REQUIRES_NEW method is called directly from the same bean, does it get a new transaction?
    No. @Transactional is proxy-based; a self-call bypasses the proxy, so the annotation is ignored and the code runs in the outer transaction. You must invoke it across a bean boundary.

saying these in an interview costs you the question

  • Saying REQUIRES_NEW reuses the same connection with a savepoint (that's NESTED)
  • Thinking the outer connection is returned to the pool while suspended
  • Claiming an inner rollback also rolls back the outer transaction
  • Assuming a self-invocation still triggers a new transaction

context