skip to content

What are the semantics and risks of rollbackForClassName / noRollbackForClassName string matching?

level: seniorimportance: nice to knowfreq 22%

answer

  1. String pattern = substring of class name
  2. Walks up superclasses computing depth
  3. "Exception" matches almost everything
  4. Even Class rules match by getName()
  5. No regex/wildcards — plain contains()

basics

~20 s

They match by exception class-name substring, not exact type. Spring walks up the exception's superclasses and matches if a class name CONTAINS the given string. That makes short patterns like "Exception" match almost everything, so use specific patterns.

solid answer

~40 s

rollbackForClassName and noRollbackForClassName take String patterns instead of Class objects. Spring's RollbackRuleAttribute.getDepth matches when a class name in the exception's hierarchy CONTAINS the pattern as a substring. So "ServiceException" matches com.acme.ServiceException and also OrderServiceException; "Exception" matches nearly every throwable. The upside is decoupling — you don't need the class on the classpath at config time, handy for framework/library exceptions or config-driven setups. The risk is accidental over-matching and, combined with the closest-match-wins depth logic, surprising commit/rollback decisions. Prefer the typed rollbackFor/noRollbackFor when you can reference the class; when using the String form, use fully-qualified or clearly distinctive names to avoid substring collisions.

code

java · 11 lines
java
// Decoupled from a class you can't/won't import:
@Transactional(rollbackForClassName = "com.acme.import.ImportFailure")
public void runImport() { ... }

// DANGER: substring match — this rolls back on nearly EVERYTHING
@Transactional(rollbackForClassName = "Exception")
public void oops() { ... }

// Accidental collision: matches BOTH OrderException and BigOrderException
@Transactional(noRollbackForClassName = "OrderException")
public void reserve() { ... }

go deeper

for a junior

Not expected to know the String variants exist.

for a middle

Aware they exist and take names instead of classes.

for a senior

Understands substring matching, the depth walk, and the over-matching risk.

for a principal

Sets a convention (typed rules preferred; fully-qualified names when strings are unavoidable) to prevent accidental transaction-outcome flips.

## String-based rollback rules `@Transactional` offers String-valued cousins of the typed attributes: - `rollbackForClassName = "..."` - `noRollbackForClassName = "..."` Internally these still create `RollbackRuleAttribute` / `NoRollbackRuleAttribute` — the **same** objects the typed `Class` attributes create. In fact, even the `Class`-based rules store the class's **name** (`clazz.getName()`) and match by name. So all rule matching is ultimately **name-substring based**. ### Matching algorithm (`RollbackRuleAttribute.getDepth`) Starting from the thrown exception's class, Spring checks whether the class's fully-qualified name **contains** the pattern string. If not, it moves to the superclass and increments a depth counter, repeating up to `Throwable`. - Match at the thrown class = depth 0. - Match one superclass up = depth 1, etc. - Never contains it = -1 (no match). Because it's `String.contains`, the pattern need not be fully qualified — `"OrderException"` matches `com.acme.OrderException`. ### Why the substring behavior is risky - `"Exception"` is a substring of virtually every exception class name -> matches essentially everything at some depth. - `"UserException"` also matches `SuperUserException`, `PowerUserException`, etc. - A too-broad pattern can win the **closest-match** contest unexpectedly, flipping a commit/rollback decision (see the RuleBasedTransactionAttribute depth logic). ### When the String form is genuinely useful - The exception type isn't available at compile time (e.g. an optional dependency, or a driver-specific exception). - Configuration-driven or XML setups (`<tx:method rollback-for="..."/>`). - Avoiding a hard compile-time dependency on a third-party library's exception. ### Best practices 1. Prefer the **typed** `rollbackFor` / `noRollbackFor` when you can import the class — you get IDE support and refactor safety. 2. If you must use the String form, use **fully-qualified** or clearly **distinctive** names to minimize accidental substring hits. 3. Remember the matching is name-based even for `Class` rules, so relocating/renaming exception classes changes matches only if the simple/qualified name changes. ### Note The patterns are **not regex or wildcards** — plain substring containment. There's no `*` globbing.

  • Are the class-name patterns regexes or globs?
    Neither — they're plain substring (String.contains) matches against fully-qualified class names walking up the exception hierarchy. No wildcards or regex are supported.
  • Does the typed rollbackFor = X.class avoid the substring behavior?
    It's more precise in intent, but internally the rule still stores X's name and matches by name; the practical benefit is compile-time safety and no risk of a hand-typed pattern accidentally matching an unrelated class.

saying these in an interview costs you the question

  • Claiming the String patterns support wildcards or regex
  • Assuming exact fully-qualified match only (no substring)
  • Not realizing 'Exception' matches almost every throwable
  • Believing Class-based rules match by identity rather than name

context