java 97 lines · 3 tabs

Multi-Step Order Flow With Spring TransactionTemplate and Savepoint Rollback

Shared by codesnips Sep 2026
3 tabs
@Configuration
public class TxConfig {

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        DataSourceTransactionManager tm = new DataSourceTransactionManager(dataSource);
        tm.setNestedTransactionAllowed(true);
        return tm;
    }

    @Bean
    public TransactionTemplate orderTransactionTemplate(PlatformTransactionManager tm) {
        TransactionTemplate template = new TransactionTemplate(tm);
        template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        template.setTimeout(10);
        return template;
    }
}
3 files · java Explain with highlit

This snippet shows how a checkout flow that touches several aggregates can be composed with programmatic transaction control instead of a single @Transactional boundary. The OrderCheckoutService is the entry point: it wraps the whole flow in one TransactionTemplate so that stock reservation, payment authorization, and loyalty accrual either all commit or all roll back together.

The reason programmatic control is used here rather than the declarative annotation is that the flow has an optional step — loyalty points — that should be allowed to fail without aborting the entire order. Declarative propagation makes partial failure awkward, but a JDBC savepoint expresses it directly. In OrderCheckoutService, template.execute opens the outer transaction and TransactionStatus is used to createSavepoint() before the loyalty step. If LoyaltyGateway.accrue throws, the code calls rollbackToSavepoint(sp) and continues, so the order still commits with points skipped; on success it calls releaseSavepoint(sp).

The outer template is configured with PROPAGATION_REQUIRED and an isolation level chosen to protect the stock check. Savepoints only work inside an existing physical transaction, which is exactly what the template guarantees, and they require a DataSource that supports nested savepoints (nestedTransactionAllowed is enabled on the transaction manager in TxConfig). Setting template.setRollbackOnly() via status.setRollbackOnly() is used for the hard-failure path when payment is declined, forcing the entire outer transaction to unwind.

TxConfig wires a PlatformTransactionManager and exposes a preconfigured TransactionTemplate bean so services do not repeat isolation and timeout settings. The InventoryRepository performs the conditional UPDATE ... WHERE quantity >= ? that returns an affected-row count, letting the service detect an oversell atomically rather than reading-then-writing.

The key trade-off is added verbosity: the service now owns commit/rollback semantics explicitly. In return it gains fine-grained partial rollback that annotations cannot express, and the boundaries stay visible in one place. A common pitfall is calling savepoint APIs outside an active transaction, or expecting a savepoint to survive an outer rollback — it will not. This pattern fits multi-step workflows where some steps are best-effort while the core must remain strongly consistent.


Related snips

Share this code

Here's the card — post it anywhere.

Multi-Step Order Flow With Spring TransactionTemplate and Savepoint Rollback — share card
Link copied