Boost Spring Boot Throughput: Programmatic Transaction Management vs @Transactional

This article demonstrates how using Spring Boot's TransactionTemplate to programmatically control transactions can prevent long‑running non‑transactional operations from blocking database connections, thereby increasing system throughput compared to the traditional @Transactional annotation, with detailed code examples and performance observations.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Boost Spring Boot Throughput: Programmatic Transaction Management vs @Transactional

Environment: Spring Boot 2.3.9.RELEASE, JPA, MySQL.

Typical usage in a Spring project is to add @Transactional on a method or class, e.g.:

@Transactional
public Account deduction(Long id, BigDecimal money) {
    Optional<Account> op = accountDAO.findById(id);
    if (!op.isPresent()) {
        throw new RuntimeException("不存在");
    }
    account.setMoney(account.getMoney().subtract(money));
    return accountDAO.saveAndFlush(account);
}

This method‑level transaction opens a connection, begins a transaction, executes the method, then commits or rolls back.

When the connection pool is limited (e.g., only one connection), long‑running non‑transactional work can occupy the connection and drastically reduce throughput. The article simulates this low‑throughput scenario by configuring the datasource with a single HikariCP connection:

spring:
  datasource:
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/x?serverTimezone=GMT%2B8
    username: root
    password: xxxx
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      minimumIdle: 1
      maximumPoolSize: 1
      autoCommit: true
      idleTimeout: 30000
      poolName: MasterDatabookHikariCP
      maxLifetime: 1800000
      connectionTimeout: 30000
      connectionTestQuery: SELECT 1

Service method with a simulated 10‑second delay:

@Transactional
public Account deduction(Long id, BigDecimal money) {
    System.out.println("Service 当前执行线程:" + Thread.currentThread().getName() + ", id = " + id + ", money = " + money);
    Account account = accountDAO.findById(id).orElse(null);
    if (account == null) {
        return null;
    }
    try {
        TimeUnit.SECONDS.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    account.setMoney(account.getMoney().subtract(money));
    return accountDAO.saveAndFlush(account);
}

Controller endpoint:

@GetMapping("/deduction")
public Object deductionAction(Long id, BigDecimal money) {
    System.out.println("Controller 当前线程:" + Thread.currentThread().getName());
    return accountService.deduction(id, money);
}

Testing with two browsers shows that only one service method proceeds because the single connection is held by the transaction, while the other request waits.

To improve throughput, the article replaces the annotation with programmatic transaction control using TransactionTemplate:

@Resource
private TransactionTemplate transactionTemplate;

public Account deduction(Long id, BigDecimal money) {
    System.out.println("Service 当前执行线程:" + Thread.currentThread().getName() + ", id = " + id + ", money = " + money);
    Account account = accountDAO.findById(id).orElse(null);
    if (account == null) {
        return null;
    }
    try {
        TimeUnit.SECONDS.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    // Non‑transactional part finished, now execute the DB update in a transaction
    return transactionTemplate.execute(status -> {
        try {
            account.setMoney(account.getMoney().subtract(money));
            return accountDAO.saveAndFlush(account);
        } catch (Exception e) {
            logger.error("发生错误:{}", e);
            status.setRollbackOnly();
        }
        return null;
    });
}

The @Transactional annotation is removed; only the critical update is wrapped in a transaction. Spring Boot automatically configures TransactionTemplate via TransactionAutoConfiguration.

After redeploying, both browser requests complete even with a single connection pool, because the long‑running computation runs outside the transaction, freeing the connection for other requests. This demonstrates a noticeable increase in system throughput.

Conclusion: By separating time‑consuming non‑transactional logic from the transactional block and using programmatic transaction management, you can avoid connection bottlenecks and improve overall performance.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaperformanceSpring Boottransaction-managementjpa
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.