skip to content

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