skip to content

What is DataSourceTransactionManager and when do you use it?

level: juniorimportance: must knowfreq 70%

answer

  1. one DataSource, plain JDBC/MyBatis
  2. binds Connection to thread via DataSourceUtils
  3. setAutoCommit(false) then commit/rollback
  4. Boot auto-configures it when no JPA
  5. not for JPA -> JpaTransactionManager

basics

~10 s

It is Spring's PlatformTransactionManager for a single JDBC DataSource. It opens a database Connection, turns off auto-commit, and commits or rolls back when the transaction ends. Use it with JdbcTemplate or MyBatis.

solid answer

~40 s

DataSourceTransactionManager is the concrete PlatformTransactionManager you register when your app talks to one relational database through plain JDBC (JdbcTemplate) or MyBatis — i.e. no JPA/Hibernate. On a @Transactional method it obtains a Connection from the DataSource, disables auto-commit, and binds that Connection to the current thread. All JdbcTemplate calls in that method reuse the bound Connection, so they share one transaction. On normal return it calls Connection.commit(); on a runtime exception it calls Connection.rollback(); then it restores auto-commit and returns the Connection. In Spring Boot it is auto-configured automatically when a DataSource is present and JPA is not, so you rarely declare it by hand — but you must use it (not JpaTransactionManager) for pure-JDBC stacks.

code

java · 24 lines
java
@Configuration
public class TxConfig {

    // In Spring Boot this bean is auto-configured; shown for clarity.
    @Bean
    public DataSourceTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

@Service
public class AccountService {
    private final JdbcTemplate jdbc;

    public AccountService(JdbcTemplate jdbc) { this.jdbc = jdbc; }

    @Transactional // driven by DataSourceTransactionManager
    public void transfer(long from, long to, int cents) {
        // Both statements reuse the same thread-bound Connection ->
        // they commit or roll back together.
        jdbc.update("UPDATE account SET balance = balance - ? WHERE id = ?", cents, from);
        jdbc.update("UPDATE account SET balance = balance + ? WHERE id = ?", cents, to);
    }
}

go deeper

for a junior

Know it is the tx manager for plain JDBC/JdbcTemplate on one database and that it commits/rolls back automatically.

for a middle

Explain the thread-bound Connection and that Boot auto-configures it when JPA is absent.

for a senior

Contrast with JpaTransactionManager and JtaTransactionManager and explain DataSourceUtils sharing.

for a principal

Reason about single-resource limits, XA alternatives, and library integration via TransactionAwareDataSourceProxy.

## The problem it solves A database transaction lives on a single JDBC `Connection`. To make several DAO calls part of one atomic unit, they must all use the *same* `Connection`, and something must call `commit()` or `rollback()` at the end. `DataSourceTransactionManager` is the Spring component that manages exactly this for **one** `javax.sql.DataSource`. ## Where it fits `DataSourceTransactionManager` implements `PlatformTransactionManager` (via the base class `AbstractPlatformTransactionManager`). `PlatformTransactionManager` is the abstraction the `@Transactional` machinery drives. Spring ships several implementations: - `DataSourceTransactionManager` — plain JDBC / MyBatis, one `DataSource`. - `JpaTransactionManager` — JPA/Hibernate `EntityManager`. - `JtaTransactionManager` — distributed (XA) transactions across multiple resources. You pick the one matching your persistence technology. For `JdbcTemplate` and MyBatis, that is `DataSourceTransactionManager`. ## What happens step by step 1. A `@Transactional` method is called; the transaction interceptor asks the manager to begin a transaction (`getTransaction`). 2. The manager calls `dataSource.getConnection()`, and unless auto-commit is already off it calls `connection.setAutoCommit(false)` (auto-commit means every statement commits on its own — the opposite of a transaction). 3. It **binds** the `Connection` to the current thread using `TransactionSynchronizationManager.bindResource(dataSource, connectionHolder)`. 4. Business code runs. Every `JdbcTemplate`/MyBatis statement internally calls `DataSourceUtils.getConnection(dataSource)`, which **returns the already-bound thread Connection** instead of opening a new one. That is how many DAO calls share one transaction. 5. On success the interceptor calls `commit`, which triggers `connection.commit()`. On a runtime exception (or an exception listed in `rollbackFor`) it calls `rollback` → `connection.rollback()`. 6. Finally the manager unbinds the resource, restores `autoCommit=true`, and releases the `Connection` back to the pool via `DataSourceUtils.releaseConnection`. ## Key collaborators (define the jargon) - **`DataSourceUtils`** — a static utility. `getConnection(ds)` returns the transaction's bound `Connection` if one exists, else a fresh one; `releaseConnection(con, ds)` closes it *only if* it is not participating in a managed transaction. `JdbcTemplate` uses these, which is why plain JdbcTemplate participates in Spring transactions automatically. - **`TransactionSynchronizationManager`** — the `ThreadLocal` registry that holds the bound `Connection` (and synchronization callbacks) for the current thread. This thread-binding is why transactions are thread-confined and don't cross to another thread unless you propagate them. - **`ConnectionHolder`** — the object stored in the thread local that wraps the `Connection` and a reference count. ## Spring Boot auto-configuration `DataSourceTransactionManagementAutoConfiguration` registers a `DataSourceTransactionManager` bean when a `DataSource` is on the classpath and no JPA `EntityManagerFactory` is present. So in a Boot JDBC app you normally just annotate methods with `@Transactional` and never touch the manager. In a manual `@Configuration` you declare it yourself. ## When to use vs. not - **Use** for JdbcTemplate, `NamedParameterJdbcTemplate`, or MyBatis against a single database. - **Do not use** if your primary persistence is JPA — use `JpaTransactionManager` so the `EntityManager` and its flush are enrolled. (`JpaTransactionManager` still manages the underlying JDBC `Connection` too, so `JdbcTemplate` sharing the same `DataSource` will join the JPA transaction.) - For **two or more** transactional resources that must commit atomically, you need `JtaTransactionManager` (XA), not `DataSourceTransactionManager`. ## Gotchas - It manages exactly **one** `DataSource`. If you have two databases you need two managers and must qualify `@Transactional("tmA")`. - A raw `dataSource.getConnection()` in your code bypasses the bound Connection and runs outside the transaction; always go through `JdbcTemplate` or `DataSourceUtils`, or wrap the DataSource in `TransactionAwareDataSourceProxy` when a third-party library insists on calling `getConnection()` itself. - Transactions are thread-bound; work handed to another thread (e.g. `@Async`, a new executor) is a *new* transaction context, not the caller's.

  • How does a second JdbcTemplate call in the same method end up on the same Connection?
    JdbcTemplate calls DataSourceUtils.getConnection(dataSource), which returns the ConnectionHolder already bound to the thread by the transaction manager, instead of opening a new Connection.
  • Do you need to declare this bean in Spring Boot?
    Usually no. Boot auto-configures a DataSourceTransactionManager when a DataSource exists and JPA is absent. You declare it manually only in non-Boot configs or when overriding defaults.

saying these in an interview costs you the question

  • Saying DataSourceTransactionManager works for JPA/Hibernate entities (that needs JpaTransactionManager).
  • Claiming it can span two databases atomically (that requires JTA/XA).
  • Thinking you must manually pass the Connection between DAO methods.

context