When would you choose REQUIRES_NEW over REQUIRED, and what does that change about connections and commit boundaries?
answer
- REQUIRES_NEW = suspend + new Connection
- Independent commit / survives outer rollback
- Audit, outbox, failure logging
- Two Connections held -> pool exhaustion risk
- Self-invocation bypasses the proxy
basics
~20 sUse REQUIRES_NEW when a piece of work must commit or roll back independently of the caller — like an audit log that should persist even if the main transaction fails. It suspends the current transaction and runs on a second, separate Connection.
solid answer
~40 sREQUIRED joins the caller's transaction: one Connection, one commit, all-or-nothing. REQUIRES_NEW instead suspends the current transaction and starts a brand-new physical transaction on a second Connection, which commits or rolls back on its own. You pick REQUIRES_NEW when the inner work must be durable regardless of the outer outcome (audit trails, outbox rows, failure logging) or must be isolated so its failure doesn't doom the outer. The costs: it holds two Connections from the pool at once for the nesting depth, so deep or looped REQUIRES_NEW can exhaust the pool and even self-deadlock; the two transactions can't see each other's uncommitted changes; and it only works via the proxy (self-invocation bypasses it). REQUIRED remains the default for cohesive units of work; REQUIRES_NEW is a deliberate carve-out for independent commits.
code
java · 21 lines@Service
public class PaymentService {
private final AuditService audit;
@Transactional // REQUIRED: the main unit of work
public void pay(Payment p) {
debit(p);
// record the attempt so it survives even if debit later rolls back
audit.recordAttempt(p); // separate physical tx, separate Connection
chargeGateway(p); // if this throws, pay() rolls back...
} // ...but the audit row already committed independently
}
@Service
class AuditService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void recordAttempt(Payment p) {
// suspends pay()'s transaction, commits on its own Connection
}
}go deeper
Know REQUIRES_NEW makes an independent transaction, unlike REQUIRED.
Explain suspend/resume and the separate Connection and commit boundary.
Weigh use cases (audit/outbox) against pool exhaustion, visibility, and self-invocation caveats.
Design failure/commit domains deliberately, size the connection pool for nesting depth, and choose NESTED vs REQUIRES_NEW by cost.
## Contrast in one line - **REQUIRED**: 'Be part of the caller's transaction.' Join if one exists; else start one. Single Connection, single commit boundary. - **REQUIRES_NEW**: 'Always run in my own transaction.' Suspend any current transaction and begin a fresh physical one on a **new Connection**; commit/rollback it independently, then resume the suspended one. ## What suspension means When a REQUIRES_NEW method is entered while a transaction is active, the `PlatformTransactionManager` **suspends** the outer transaction: it unbinds the outer Connection/resources from the thread (holding them aside) and binds a **second** Connection for the inner transaction. When the inner method returns, the inner transaction commits (or rolls back) on its own Connection, that Connection is released, and the outer transaction is **resumed** (rebound to the thread). ## When to choose REQUIRES_NEW 1. **Independent durability** — the inner work must survive even if the outer rolls back. Classic cases: writing an **audit/log record**, an **outbox message**, recording a **failed-attempt counter**. If it were REQUIRED, an outer rollback (or the rollback-only trap) would erase it. 2. **Failure isolation** — you want to call something that may fail, catch it, and continue the outer transaction. Under REQUIRED that triggers `UnexpectedRollbackException`; under REQUIRES_NEW the inner rolls back its own transaction and the outer stays committable. ## When NOT to (the costs) - **Two Connections held at once.** During the inner call, the pool lends out the suspended outer Connection *plus* the new inner one. Nesting or looping REQUIRES_NEW multiplies this. With a small pool (e.g. HikariCP `maximumPoolSize=10`), a loop of REQUIRES_NEW calls can **exhaust the pool and deadlock** — the outer holds a Connection while waiting for the inner to get one that will never free up. - **No shared visibility.** The inner transaction can't see the outer's uncommitted writes, and vice versa — they're separate DB transactions. This can cause surprising lock waits (the inner may block on rows the outer locked). - **Proxy-only.** Like all Spring `@Transactional`, propagation is applied by the AOP proxy. A **self-invocation** (`this.inner()` inside the same bean) bypasses the proxy, so REQUIRES_NEW is silently ignored and the call just runs in the existing transaction. Call across beans (or self-inject the proxy) to make it take effect. - **Performance** — extra Connection acquisition, suspend/resume overhead. ## Related propagations for comparison - **NESTED**: a single Connection with a **savepoint**; inner rollback rewinds to the savepoint but stays part of the outer transaction — cheaper than REQUIRES_NEW when you only need partial rollback, not independent commit. Requires savepoint support (`DataSourceTransactionManager`; JPA support varies). - **MANDATORY / SUPPORTS / NOT_SUPPORTED / NEVER** — other members of the propagation family, not the focus here. ## Decision guide - Cohesive unit of work → **REQUIRED** (default). - Must commit independently / survive outer rollback → **REQUIRES_NEW**. - Just need to undo the inner part on failure but keep it in the same transaction → **NESTED**. ## Interview signal Strong answers pair the semantic difference (independent commit, separate Connection) with the operational hazards (connection-pool exhaustion, no cross-visibility, self-invocation) and can contrast NESTED as the cheaper 'partial rollback' option.
- Why can a loop that calls a REQUIRES_NEW method deadlock?Each iteration holds the suspended outer Connection plus a new inner one. With a bounded pool, outstanding suspended Connections plus new requests can exhaust the pool, and the outer waits forever for an inner Connection that never frees.
- If a bean calls its own REQUIRES_NEW method via this.method(), does a new transaction start?No. Self-invocation doesn't go through the AOP proxy, so the propagation is ignored and the call runs in the existing transaction. Call via the injected proxy or another bean.
- When is NESTED a better fit than REQUIRES_NEW?When you only need to roll back the inner work on failure while keeping it part of the outer transaction and one Connection — NESTED uses a savepoint and doesn't commit independently.
saying these in an interview costs you the question
- Using REQUIRES_NEW as a default 'to be safe' — it doubles connection usage and breaks atomicity.
- Expecting the inner REQUIRES_NEW transaction to see the outer's uncommitted rows.
- Forgetting self-invocation silently disables the new transaction.
- Confusing REQUIRES_NEW (independent commit, new Connection) with NESTED (savepoint, same Connection).