skip to content

How do rollbackFor and noRollbackFor change the default rollback behavior, and when would you use each?

level: middleimportance: must knowfreq 72%

answer

  1. rollbackFor = roll back on checked too
  2. noRollbackFor = commit despite unchecked
  3. noRollbackFor doesn't swallow — still propagates
  4. String variants match by name substring
  5. No rule matches -> default rule

basics

~20 s

rollbackFor adds exception types (including checked ones) that should trigger rollback. noRollbackFor names exceptions that should NOT roll back even though they normally would (e.g. a RuntimeException you treat as a business outcome). Both go on @Transactional.

solid answer

~40 s

@Transactional(rollbackFor = ...) tells Spring to roll back when the listed exception types (or subclasses) propagate — the usual use is to roll back on a checked exception the default would otherwise commit, e.g. rollbackFor = BusinessException.class. @Transactional(noRollbackFor = ...) does the opposite: it commits even when a listed exception propagates, typically an unchecked exception you want to treat as an expected, non-failing outcome. There are also String-based variants, rollbackForClassName / noRollbackForClassName, that match by class-name pattern. These attributes build rollback rules that override the default RuntimeException/Error rule; if no explicit rule matches the thrown exception, Spring falls back to the default. Crucially, noRollbackFor does not swallow the exception — it still propagates to the caller; only the commit/rollback decision changes.

code

java · 10 lines
java
// Roll back even though the exception is CHECKED
@Transactional(rollbackFor = PaymentDeclinedException.class) // extends Exception
public void pay() throws PaymentDeclinedException { ... }

// Commit even though the exception is a RuntimeException
@Transactional(noRollbackFor = StockWarningException.class)  // extends RuntimeException
public void reserve() {
    saveAuditRow();                       // we WANT this persisted
    throw new StockWarningException();    // business signal, still propagates to caller
}

go deeper

for a junior

Knows the two attributes exist and what each does at a high level.

for a middle

Can pick the right one per scenario and knows noRollbackFor still propagates.

for a senior

Understands the String-variant substring matching risk and supertype coverage.

for a principal

Weighs a project-wide policy (e.g. rollbackFor=Exception.class) vs. per-method rules and its blast radius.

## Overriding the default with rules The default rule (rollback on unchecked/Error, commit on checked) is often not what you want. `@Transactional` lets you attach explicit **rollback rules** that override it. ### `rollbackFor` ```java @Transactional(rollbackFor = BusinessException.class) ``` Adds a rule: if a `BusinessException` **or any subclass** propagates out, **roll back** — even though it's a checked exception the default would have committed. You can list multiple: `rollbackFor = {IOException.class, ParseException.class}`. A common project-wide choice is `rollbackFor = Exception.class` to make *every* propagating exception roll back. ### `noRollbackFor` ```java @Transactional(noRollbackFor = InsufficientStockException.class) ``` Adds a rule: if this exception (or subclass) propagates, **commit anyway**, even though — being a `RuntimeException` — it would normally roll back. Use it when an exception is really a *business signal*, not a failure: you still want the work done so far to persist, and you throw only to communicate an outcome to the caller. ### `rollbackForClassName` / `noRollbackForClassName` String variants that match by **class-name pattern** (substring match against the exception's class name, walking up its superclasses). Useful when you can't or don't want to reference the class at compile time: ```java @Transactional(rollbackForClassName = "BusinessException") ``` Beware: because it's a substring match, `"Exception"` would match nearly everything, and `"UserException"` could accidentally match `SuperUserException`. ### Key semantics to remember 1. **Rules match by type assignability (or name)** — a rule for a supertype also covers its subtypes. 2. **noRollbackFor does NOT catch or swallow.** The exception still propagates to the caller exactly as thrown; only the transaction outcome (commit vs rollback) is affected. 3. **If no explicit rule matches**, Spring falls back to the default `RuntimeException`/`Error` rule. 4. These override rules also work in XML/`<tx:method rollback-for=.../>` and are the same mechanism used programmatically via `RollbackRuleAttribute` / `NoRollbackRuleAttribute`. ### When to use which - **rollbackFor**: you throw checked exceptions for business errors but still want atomicity — most common with legacy/checked-exception codebases. - **noRollbackFor**: a runtime exception is an *expected* outcome and you want partial work (e.g. an audit row, a status update) committed. ### Gotcha: it only applies at the transaction boundary Rules are evaluated on the exception that **escapes the transactional method**. Catch-and-swallow inside the method bypasses the whole thing.

  • Does noRollbackFor make the exception disappear for the caller?
    No. The exception still propagates up the call stack exactly as thrown; noRollbackFor only tells Spring to commit instead of roll back. If you want the caller to see nothing, you must catch it yourself.
  • What's the risk of rollbackForClassName = "Exception"?
    It's a substring name match walking the class hierarchy, so 'Exception' matches almost every throwable — it would effectively roll back on everything, which may or may not be intended, and is easy to trigger accidentally.

saying these in an interview costs you the question

  • Saying noRollbackFor swallows/hides the exception from the caller
  • Thinking rollbackFor is required to roll back on RuntimeExceptions (it already does by default)
  • Believing rollbackForClassName does exact fully-qualified matching only
  • Assuming a rule for a supertype doesn't cover subtypes

context