skip to content

Multiple & Chained Transaction Managers

With several transaction managers you must name one per @Transactional, and chaining them is best-effort 1PC that can commit one and fail the next. Interviewers ask to see whether you would claim atomicity you do not actually have.

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

questions

4

You have two PlatformTransactionManager beans in your Spring context. How does @Transactional know which one to use, and how do you point a method at a specific one?

level: juniorimportance: must knowfreq 55%

answer

  1. value == transactionManager alias
  2. qualifier matches bean name or @Qualifier
  3. @Primary or TransactionManagementConfigurer = default
  4. bare + multiple + no default = resolution error
  5. qualifier selects, does not merge

basics

~10 s

By default Spring needs one clear choice. To pick a specific manager, pass its bean name as a qualifier: @Transactional("orderTxManager") or @Transactional(transactionManager = "orderTxManager").

solid answer

~40 s

A PlatformTransactionManager is the bean Spring uses to begin/commit/rollback. With one such bean, @Transactional uses it automatically. With several, you disambiguate by giving @Transactional a qualifier string: @Transactional("orderTx") (shorthand for the transactionManager attribute). That string is matched against a bean name or a @Qualifier value on the manager bean. If you leave it blank and there are multiple candidates with no @Primary and no default configured, Spring fails to resolve a unique manager. You can set a default for the whole app by implementing TransactionManagementConfigurer and returning the manager from annotationDrivenTransactionManager(), or by marking one bean @Primary. So: name/qualifier for per-method selection, @Primary or TransactionManagementConfigurer for the default.

code

java · 26 lines
java
@Configuration
public class TxConfig {

    @Bean
    public PlatformTransactionManager orderTxManager(
            @Qualifier("orderDataSource") DataSource ds) {
        return new DataSourceTransactionManager(ds);
    }

    @Bean
    public PlatformTransactionManager analyticsTxManager(
            @Qualifier("analyticsDataSource") DataSource ds) {
        return new DataSourceTransactionManager(ds);
    }
}

@Service
class OrderService {

    // picks orderTxManager by name/qualifier
    @Transactional("orderTxManager")
    public void placeOrder(Order o) { /* writes to orders DB */ }

    @Transactional(transactionManager = "analyticsTxManager")
    public void recordMetric(Metric m) { /* writes to analytics DB */ }
}

go deeper

for a junior

Know that value/transactionManager is a qualifier string selecting the manager bean by name.

for a middle

Explain @Primary vs TransactionManagementConfigurer for defaults and the ambiguity failure.

for a senior

Stress that a qualifier selects one manager only and does not coordinate multiple datasources.

for a principal

Frame per-method qualifiers as the safe multi-datasource default; escalate to chaining/JTA only when a single unit of work truly spans datasources.

**PlatformTransactionManager** is the core Spring interface that actually starts, commits, and rolls back transactions (implementations include `DataSourceTransactionManager` for plain JDBC, `JpaTransactionManager` for JPA, `JtaTransactionManager` for XA/global transactions). `@Transactional` is only an *instruction*; the transaction manager bean does the work, driven by a proxy around your bean. **Single manager (the common case):** If exactly one `PlatformTransactionManager` bean exists, `@Transactional` finds it by type and uses it. No qualifier needed. **Multiple managers:** When you connect to two databases (say `orders` and `analytics`), you typically declare two managers, e.g. `orderTxManager` and `analyticsTxManager`. Now a bare `@Transactional` is ambiguous. Spring resolves the manager using the annotation's **qualifier**: - `@Transactional("orderTxManager")` — the single `value` attribute is an alias for `transactionManager`. - `@Transactional(transactionManager = "orderTxManager")` — explicit form. The qualifier string is matched first against a bean **name**, and also against any `@Qualifier("...")` declared on the manager bean definition. This is a Spring *qualifier* match, not a hard bean-id lookup, so you can put `@Qualifier("orders")` on the bean and reference `@Transactional("orders")`. **Choosing a default among many:** Two ways: 1. Mark one manager `@Primary`. Bare `@Transactional` then uses it. 2. Implement `TransactionManagementConfigurer` on a `@Configuration` class and return the default from `annotationDrivenTransactionManager()`. This is the most explicit way to declare 'the default transaction manager' when several exist. **Failure mode:** With multiple candidates, no `@Primary`, no `TransactionManagementConfigurer`, and a bare `@Transactional`, Spring cannot pick one and you get a resolution error (effectively a `NoUniqueBeanDefinitionException`-style failure) when the transactional proxy tries to obtain its manager. **Gotcha:** The qualifier only selects *which* manager; it does not join two managers into one transaction. A method annotated `@Transactional("orderTxManager")` runs entirely under the orders manager — writes to the analytics datasource in that method are **not** part of that transaction. Coordinating two managers is a separate problem (chaining or JTA/XA). **When to use:** Multiple managers + per-method qualifiers is the standard, safe pattern for multi-datasource apps where each unit of work touches one datasource at a time.

  • What happens if you write @Transactional with no qualifier and there are two managers with none marked @Primary?
    Spring can't resolve a unique PlatformTransactionManager, so applying the transactional advice fails (a NoUniqueBeanDefinition-style error). Add a qualifier, mark one @Primary, or implement TransactionManagementConfigurer.
  • Does @Transactional("orderTxManager") make writes to a second datasource in the same method transactional?
    No. That method runs solely under the orders manager. Writes to any other datasource are outside that transaction and won't roll back with it.

saying these in an interview costs you the question

  • Thinking @Transactional's value attribute names an isolation level or propagation, not the manager
  • Believing a qualifier merges two managers into a single transaction
  • Assuming Spring silently picks the 'first' manager when several exist

context

open as a page

What is ChainedTransactionManager and what guarantee does it actually provide across several transaction managers?

level: middleimportance: should knowfreq 45%

basics

~10 s

ChainedTransactionManager (from Spring Data) wraps several PlatformTransactionManagers so one @Transactional begins and commits them together. It gives best-effort one-phase commit, not true atomicity.

open as a page

Walk through exactly what can go wrong with ChainedTransactionManager during commit. Why is it non-atomic, and which resource is at risk?

level: seniorimportance: should knowfreq 40%

basics

~20 s

It commits each manager one at a time. If an earlier one commits and a later commit fails, the earlier commit is already durable and cannot be undone, so you get a partially-committed, inconsistent state.

open as a page

A service must atomically persist to a database and publish an event. The team proposes ChainedTransactionManager. As the architect, how do you respond and what do you recommend instead?

level: principalimportance: should knowfreq 30%

basics

~10 s

Chained gives only best-effort 1PC, so a mid-commit failure can persist the row but drop the event. For true 'both or neither', use a transactional outbox (same DB transaction) or JTA/XA. Chaining is deprecated.

open as a page