CodeGym /Courses /Module 5. Spring /Programmatic transaction management: TransactionTemplate

Programmatic transaction management: TransactionTemplate

Module 5. Spring
Level 6 , Lesson 4
Available

When we talk about programmatic transaction management, we mean taking direct control over transactions in code. Unlike the declarative approach, where you rely on Spring magic and the @Transactional annotation, here you explicitly control what, how, and when happens with your transaction.

Let's go over the main scenarios where programmatic transaction management actually makes sense:

  1. Flexibility. Sometimes you have complex transaction logic that's hard to express with declarations.
  2. Partial transactions. You need to run some operations inside a transaction and others outside of it.
  3. Optimization. You don't need a transaction for every method, and manual control helps avoid unnecessary overhead.

And if that's the case, TransactionTemplate has your back.


What is TransactionTemplate?

TransactionTemplate is a Spring class that makes programmatic transaction management easier. It provides an API that lets you wrap business logic in transactions without forcing you to deal with low-level APIs (like Connection or TransactionManager).

Here are the main perks of TransactionTemplate:

  • Easy to use: you don't have to manually open/close transactions.
  • Fewer mistakes: Spring handles the infrastructure stuff, leaving you to focus on business logic.
  • Simple configuration: you can set transaction rules (like isolation, timeouts, etc.) right in code.

How to use TransactionTemplate

Before we dive into details, here's an example:


@Component
public class TransactionalService {

    private final TransactionTemplate transactionTemplate;

    public TransactionalService(PlatformTransactionManager transactionManager) {
        // Initialize TransactionTemplate using TransactionManager
        this.transactionTemplate = new TransactionTemplate(transactionManager);
    }

    public void executeTransactionalLogic() {
        transactionTemplate.execute(status -> {
            // Run business logic inside this transaction
            performDatabaseOperation1();
            performDatabaseOperation2();

            // If needed, you can trigger a rollback manually
            // status.setRollbackOnly();

            return null; // Return a result (can be any object or null)
        });
    }

    private void performDatabaseOperation1() {
        // Logic for the first DB operation
    }

    private void performDatabaseOperation2() {
        // Logic for the second DB operation
    }
}

In the code:

  1. Initializing TransactionTemplate. To work with TransactionTemplate you need a PlatformTransactionManager (for example, JpaTransactionManager if you're using JPA/Hibernate). We pass it into the service constructor and create a TransactionTemplate instance.
  2. The execute method. All code you want to run inside the transaction is wrapped in a call to transactionTemplate.execute(). This method accepts a TransactionCallback implemented as a lambda. Everything inside the lambda runs within the transaction.
  3. Transaction rollbacks. If an error happens inside the transactional logic, Spring will automatically call rollback. If you want to force a rollback yourself, call status.setRollbackOnly().

Configuring TransactionTemplate

Isolation, timeouts and readOnly

Just like with the @Transactional annotation, you can set transaction options via TransactionTemplate. Example:


@Transactional
public class ConfigurableTransactionalService {

    private final TransactionTemplate transactionTemplate;

    public ConfigurableTransactionalService(PlatformTransactionManager transactionManager) {
        this.transactionTemplate = new TransactionTemplate(transactionManager);

        // Set isolation level
        this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);

        // Transaction timeout (in seconds)
        this.transactionTemplate.setTimeout(5);

        // Read-only transaction
        this.transactionTemplate.setReadOnly(true);
    }

    public void executeReadOnlyTransaction() {
        transactionTemplate.execute(status -> {
            performReadOperation();
            return null;
        });
    }

    private void performReadOperation() {
        // Transaction for read operations
    }
}

Details:

  • setIsolationLevel. Configures the isolation level for the transaction (e.g. ISOLATION_READ_COMMITTED, ISOLATION_SERIALIZABLE, etc.). Important for handling concurrent requests.
  • setTimeout. Defines how long a transaction can run before it's automatically rolled back.
  • setReadOnly. Helps optimize performance when the transaction is used only for reading data.

Example: partial transactionality

Say you're building an online shop and you need to:

  1. Create an order inside a transaction.
  2. Send an email notification to the user outside the transaction (so a long-running process doesn't block the DB).

Here's how you can do that with TransactionTemplate:


@Component
public class OrderService {

    private final TransactionTemplate transactionTemplate;
    private final EmailService emailService;

    public OrderService(PlatformTransactionManager transactionManager, EmailService emailService) {
        this.transactionTemplate = new TransactionTemplate(transactionManager);
        this.emailService = emailService;
    }

    public void placeOrder(Order order) {
        transactionTemplate.execute(status -> {
            // DB operations inside the transaction
            saveOrder(order);
            updateInventory(order);

            return null;
        });

        // Operations outside the transaction
        emailService.sendOrderConfirmation(order.getEmail());
    }

    private void saveOrder(Order order) {
        // Logic for saving the order
    }

    private void updateInventory(Order order) {
        // Logic for updating inventory
    }
}

Issues and mistakes when using TransactionTemplate

Error: rollback isn't triggered. If an exception occurs inside transactional code, make sure that exception is unchecked (a subclass of RuntimeException). By default Spring won't roll back transactions for checked exceptions. You can configure that with the @Transactional annotation using rollbackFor, or call status.setRollbackOnly() manually.

Error: a read-only transaction changed data. If you set setReadOnly(true) and then wrote to the DB, Spring might not reject the changes (behavior depends on the DB). Use readOnly only for clearly read-only transactions.


Practice: using TransactionTemplate in a real app

Let's add programmatic transaction control to our app. Suppose we have a library management system and we need to:

  1. Register a new user.
  2. At the same time, issue the first book to the user.

Example:


@Service
public class LibraryService {

    private final TransactionTemplate transactionTemplate;
    private final UserRepository userRepository;
    private final BookRepository bookRepository;

    public LibraryService(PlatformTransactionManager transactionManager,
                          UserRepository userRepository,
                          BookRepository bookRepository) {
        this.transactionTemplate = new TransactionTemplate(transactionManager);
        this.userRepository = userRepository;
        this.bookRepository = bookRepository;
    }

    public void registerUserAndIssueBook(User user, Book book) {
        transactionTemplate.execute(status -> {
            userRepository.save(user); // Save the user
            book.setIssuedTo(user);    // Update book status
            bookRepository.save(book); // Persist book changes

            return null;
        });
    }
}

When to use TransactionTemplate?

So, when should you pick TransactionTemplate over @Transactional?

  • You need more control over the transaction — e.g., running parts of the logic outside the transaction.
  • You're writing a library or framework and want to make transaction management reusable.
  • Your transactions are complex and need manual handling (like partial rollbacks).

For most cases declarative management with @Transactional is enough, but it's nice to have a tool like TransactionTemplate in your toolbox for tricky scenarios.

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION