Spring Frameworkcritical

@Transactional: Propagation & Isolation

@Transactional marks a method to run inside a database transaction. Spring manages transaction begin/commit/rollback via AOP. By default, it rolls back on RuntimeException and Error, but NOT on checked exceptions.

Memory anchor

@Transactional = a bodyguard at your office door who handles all visitors. Self-invocation (this.method()) is sneaking through the back door -- the bodyguard never sees you, so no transaction protection kicks in.

Expected depth

Propagation levels (most important): REQUIRED (default — join existing or create new), REQUIRES_NEW (always create new, suspend existing), NESTED (savepoint within existing), MANDATORY (must exist), SUPPORTS, NOT_SUPPORTED, NEVER. Isolation levels: READ_UNCOMMITTED, READ_COMMITTED (default in most DBs), REPEATABLE_READ, SERIALIZABLE. Higher isolation prevents more anomalies (dirty read, non-repeatable read, phantom read) but increases lock contention. rollbackFor can extend rollback to checked exceptions; noRollbackFor can prevent rollback for specific runtime exceptions.

Deep — senior internals

AOP proxy mechanism: Spring wraps the bean in a JDK dynamic proxy (if interface exists) or CGLIB proxy. The proxy intercepts calls from outside the bean, starts/commits the transaction, and delegates to the real method. Self-invocation bypasses the proxy entirely — a @Transactional method called by another method in the same class runs without a transaction (or joins the existing outer one, but does NOT trigger a new propagation). Fix: inject the bean into itself (ugly), use AopContext.currentProxy(), or restructure into separate beans. Transaction synchronization: Spring registers callbacks (afterCommit, beforeCompletion) via TransactionSynchronizationManager — useful for sending events only after commit.

🎤Interview-ready answer

@Transactional works via AOP proxies — Spring intercepts calls to annotated methods from outside the bean. Self-invocation (this.method()) bypasses the proxy and ignores @Transactional. REQUIRES_NEW suspends the current transaction and opens a new one — changes in the inner transaction commit/rollback independently; this is useful for audit logging that must persist even if the outer transaction rolls back. By default, only unchecked exceptions trigger rollback; add rollbackFor = Exception.class to rollback on checked exceptions.

Common trap

@Transactional on a private method is silently ignored — the proxy cannot intercept private calls. It must be on a public method of a Spring bean, called from outside the bean.

Related concepts