skip to content

What is manager-driven programmatic transaction management, and how do you use PlatformTransactionManager directly?

level: juniorimportance: should knowfreq 35%

answer

  1. inject PlatformTransactionManager
  2. getTransaction / commit / rollback
  3. DefaultTransactionDefinition + TransactionStatus
  4. no proxy, boundary in code
  5. you must call rollback yourself

basics

~10 s

Instead of the @Transactional annotation, you inject PlatformTransactionManager, call getTransaction(...) to start a transaction, run your code, then call commit() on success or rollback() on failure yourself.

solid answer

~40 s

Manager-driven programmatic transactions mean you control the transaction boundary in code rather than declaratively. You inject a PlatformTransactionManager bean, build a DefaultTransactionDefinition (propagation, isolation, timeout, read-only), and call txManager.getTransaction(def) which returns a TransactionStatus handle. You wrap your work in try/catch: on success call txManager.commit(status); in the catch call txManager.rollback(status) and rethrow. This is the lowest-level Spring transaction API — no AOP proxy is involved, so the boundary is explicit and visible in the method body. It is more verbose than @Transactional or TransactionTemplate but gives full control: you decide exactly when to begin, commit, or roll back, which is useful for multiple independent transactions in a loop or conditional commit logic.

code

java · 27 lines
java
@Service
public class TransferService {

    private final PlatformTransactionManager txManager;
    private final AccountDao dao;

    public TransferService(PlatformTransactionManager txManager, AccountDao dao) {
        this.txManager = txManager;
        this.dao = dao;
    }

    public void transfer(long from, long to, long cents) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setName("transfer");
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        TransactionStatus status = txManager.getTransaction(def);
        try {
            dao.debit(from, cents);
            dao.credit(to, cents);
            txManager.commit(status);
        } catch (RuntimeException ex) {
            txManager.rollback(status);
            throw ex;
        }
    }
}

go deeper

for a junior

Know the three methods (getTransaction/commit/rollback) and the try/catch shape.

for a middle

Should be able to configure DefaultTransactionDefinition and explain why rollback must be manual.

for a senior

Contrast with @Transactional's rule-based rollback and TransactionTemplate.

for a principal

Justify choosing this level only for cases the higher abstractions can't express.

## The concept Spring offers two styles of transaction management: **declarative** (the `@Transactional` annotation, driven by an AOP proxy) and **programmatic** (you write the transaction boundary in code). Programmatic itself has two flavors: the convenience wrapper `TransactionTemplate`, and the lowest level — **direct use of `PlatformTransactionManager`**. This leaf is about the latter, sometimes called *manager-driven* transactions. ## The core interface `org.springframework.transaction.PlatformTransactionManager` has three methods: - `TransactionStatus getTransaction(TransactionDefinition definition)` — begins (or joins) a transaction according to the definition and returns a handle. - `void commit(TransactionStatus status)` — commits. - `void rollback(TransactionStatus status)` — rolls back. Spring auto-configures a concrete implementation for you: `JpaTransactionManager` (JPA), `DataSourceTransactionManager` (plain JDBC/MyBatis), `JtaTransactionManager` (distributed), etc. You inject whichever bean exists. ## Defining the transaction `getTransaction` needs a `TransactionDefinition`. The usual concrete class is `org.springframework.transaction.support.DefaultTransactionDefinition`, whose setters configure: - `setPropagationBehavior(int)` — e.g. `TransactionDefinition.PROPAGATION_REQUIRED` (default), `REQUIRES_NEW`, `NESTED`. - `setIsolationLevel(int)` — e.g. `ISOLATION_READ_COMMITTED`. - `setTimeout(int seconds)`. - `setReadOnly(boolean)`. - `setName(String)` — shows up in monitoring/logs. ## The canonical pattern ```java DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setName("transferMoney"); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus status = txManager.getTransaction(def); try { // ... do work ... txManager.commit(status); } catch (RuntimeException ex) { txManager.rollback(status); throw ex; } ``` `TransactionStatus` is your handle to the running transaction. Useful methods: `isNewTransaction()`, `setRollbackOnly()`, `isRollbackOnly()`, `hasSavepoint()`, `createSavepoint()`. ## Key gotcha: you own the rollback decision Unlike `@Transactional` — which by default rolls back **only** on unchecked exceptions (`RuntimeException`/`Error`) and commits on checked exceptions — the manager does nothing automatically. If you don't call `rollback` yourself, nothing rolls back. So your `catch` block must be correct: catch broadly enough (usually `RuntimeException`, or `Throwable` if you must handle `Error`/checked too), roll back, and normally rethrow so callers see the failure. ## When to use it Reach for the direct manager only when `@Transactional` and `TransactionTemplate` don't fit: e.g. many independent transactions inside one loop (commit-per-item), transaction boundaries that must be opened in one place and closed in another, or framework/infrastructure code. For ordinary service methods, prefer `@Transactional`; for a single scoped block, prefer `TransactionTemplate` (less boilerplate, automatic rollback-on-RuntimeException).

  • Which PlatformTransactionManager implementation is used with JPA?
    JpaTransactionManager. For plain JDBC it's DataSourceTransactionManager, and JtaTransactionManager for distributed/JTA transactions. Spring Boot auto-configures the right one based on your persistence stack.

context