skip to content

@Transactional & @EnableTransactionManagement

@Transactional plus @EnableTransactionManagement (or Boot's auto-config) wires the interceptor that opens and closes a transaction around your method, and in proxy mode that method must be public. Expect the follow-up about what happens on a private or self-invoked one.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What does @Transactional do, and what happens around a method annotated with it?

level: juniorimportance: must knowfreq 90%

answer

  1. proxy wraps bean, TransactionInterceptor
  2. begin -> run -> commit / rollback
  3. rollback on unchecked only, not checked
  4. class-level default, method-level overrides
  5. declarative, no manual begin/commit

basics

~10 s

@Transactional tells Spring to run the method inside a database transaction: Spring starts a transaction before the method, commits if it returns normally, and rolls back if it throws a runtime exception.

solid answer

~40 s

@Transactional is declarative transaction management: instead of writing begin/commit/rollback by hand, you annotate a class or method and Spring wraps it in a proxy. When a call crosses that proxy, Spring opens a transaction (or joins an existing one, per the propagation setting), runs your code, then commits on normal return. It rolls back automatically on unchecked exceptions (RuntimeException, Error) but NOT on checked exceptions unless you configure rollbackFor. It works through Spring AOP, so a TransactionInterceptor sits around the target bean and delegates the actual commit/rollback to a configured TransactionManager (e.g. JpaTransactionManager or DataSourceTransactionManager). Method-level annotations override class-level ones. You can tune propagation, isolation, readOnly, timeout, and rollback rules as attributes.

code

java · 20 lines
java
@Service
public class TransferService {

    private final AccountRepository accounts;

    public TransferService(AccountRepository accounts) {
        this.accounts = accounts;
    }

    // One atomic unit: both saves commit together, or neither does.
    @Transactional
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        Account from = accounts.findById(fromId).orElseThrow();
        Account to   = accounts.findById(toId).orElseThrow();
        from.debit(amount);   // throws if insufficient funds -> RuntimeException -> rollback
        to.credit(amount);
        accounts.save(from);
        accounts.save(to);
    }
}

go deeper

for a junior

Know that it wraps the method in a DB transaction and commits/rolls back automatically.

for a middle

Know the default rollback rule (unchecked only), class vs method placement, and the proxy mechanism.

for a senior

Be able to name the TransactionInterceptor/TransactionManager path and reason about propagation and readOnly.

for a principal

Frame it as declarative AOP-based cross-cutting concern and discuss when programmatic (TransactionTemplate) or reactive transaction management is a better fit.

## What a transaction is A database **transaction** is a unit of work that is atomic: either all its statements commit together or they are all rolled back. Without transactions, a failure halfway through a multi-step operation (e.g. debit one account, credit another) can leave data half-updated. ## Declarative vs programmatic You could manage this by hand (`connection.setAutoCommit(false)`, `commit()`, `rollback()`) — that is *programmatic* transaction management (`TransactionTemplate` is Spring's helper). `@Transactional` is *declarative*: you annotate a bean method and Spring adds the begin/commit/rollback around it for you. Less boilerplate, no transaction plumbing mixed into business logic. ## How it works mechanically `@Transactional` relies on **Spring AOP proxies**. When Spring detects transactional beans, it wraps each one in a proxy (a JDK dynamic proxy if the bean implements an interface, otherwise a CGLIB subclass proxy). The proxy holds a `TransactionInterceptor`. When an external caller invokes an annotated method: 1. The interceptor reads the method's `TransactionAttribute` (from `AnnotationTransactionAttributeSource`). 2. It asks the configured `TransactionManager` (a `PlatformTransactionManager` such as `JpaTransactionManager`, `DataSourceTransactionManager`, or `JtaTransactionManager`) to start or join a transaction per the **propagation** rule. 3. Your method body runs. 4. On normal return it commits; on a matching exception it rolls back; then it restores the previous transaction context. ## Default rollback rule (critical gotcha) Spring rolls back automatically only on **unchecked** exceptions — `RuntimeException` and `Error`. A **checked** exception (e.g. `IOException`) does NOT trigger rollback by default; the transaction commits. To change this: `@Transactional(rollbackFor = Exception.class)`. Conversely, `noRollbackFor` suppresses rollback for chosen runtime exceptions. ## Placement - On a **class**: applies to all public methods as a default. - On a **method**: overrides the class-level setting for that method. - Method-level always wins over class-level. ## Common attributes - `propagation` — REQUIRED (default: join or start), REQUIRES_NEW (suspend and start fresh), SUPPORTS, MANDATORY, NEVER, NOT_SUPPORTED, NESTED. - `isolation` — DEFAULT, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE. - `readOnly` — a hint; lets JPA/Hibernate skip dirty-checking and drivers optimize. - `timeout` — seconds before the transaction is rolled back. - `rollbackFor` / `noRollbackFor` — override the default rollback rule. ## When to use Any service method that performs multiple writes that must succeed or fail together. Put it on the **service layer**, not repositories or controllers, so one business operation equals one transaction.

  • Does @Transactional roll back on a checked exception by default?
    No. By default Spring rolls back only on RuntimeException and Error. A checked exception commits unless you add rollbackFor = Exception.class (or a more specific checked type).
  • Which layer should carry @Transactional, and why?
    The service layer, because a business operation (often spanning several repository calls) should be one transactional unit. Putting it on repositories fragments a single operation into many transactions.

saying these in an interview costs you the question

  • Claiming @Transactional rolls back on any exception, including checked ones
  • Thinking it runs the method on a background thread or asynchronously
  • Saying it uses reflection to rewrite the method rather than an AOP proxy

context

open as a page

Why does @Transactional only work on public methods in proxy mode, and why is a self-invocation ignored?

level: seniorimportance: must knowfreq 75%

basics

~20 s

In the default proxy mode, the transaction lives in a proxy that wraps the bean. The proxy can only intercept public methods, and only calls coming from outside the object. A method calling another method on 'this' skips the proxy, so @Transactional is ignored.

open as a page

What does @EnableTransactionManagement do, and do you need it in a Spring Boot app?

level: middleimportance: should knowfreq 60%

basics

~20 s

@EnableTransactionManagement turns on annotation-driven transactions by registering the AOP infrastructure that makes @Transactional work. In Spring Boot you usually do not add it — auto-configuration enables it for you when a transaction manager is present.

open as a page

Walk through how a call to a @Transactional method flows through the TransactionInterceptor and advisor at runtime.

level: seniorimportance: should knowfreq 45%

basics

~20 s

The proxy hands the call to the TransactionInterceptor. It reads the method's transaction settings, asks the TransactionManager to start or join a transaction, runs the real method, then commits on success or rolls back on a matching exception, and cleans up.

open as a page

When would you choose AdviceMode.ASPECTJ over the default proxy mode for @EnableTransactionManagement, and what are the trade-offs?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

Choose ASPECTJ when you need transactions on non-public methods or on self-invoked internal calls, which proxy mode can't advise. The cost is a weaving setup (compile-time or load-time) and more complex builds; proxy mode is simpler and the default.

open as a page