Backend Development 8 min read

Mastering Spring Transaction Timeouts: Configurations, Tests, and Internals

This article explains how to configure transaction timeout in Spring 5.3.23 using annotations and programmatic approaches, demonstrates four test scenarios with a large table, analyzes the resulting timeout exceptions, and delves into the underlying mechanisms in DataSourceTransactionManager, JdbcTemplate, and related utility classes.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Spring Transaction Timeouts: Configurations, Tests, and Internals

Environment: Spring 5.3.23

1. Configure Transaction Timeout

Annotation method:

<code>// unit is seconds
@Transactional(timeout = 2)
public void save() {
}
</code>

Programmatic method 1:

<code>@Resource
private PlatformTransactionManager tm;

DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
definition.setTimeout(2);
</code>

Programmatic method 2:

<code>@Resource
private PlatformTransactionManager tm;
public void update() {
    TransactionTemplate template = new TransactionTemplate(tm);
    template.setTimeout(2);
    template.execute(new TransactionCallback<Object>() {
        @Override
        public Object doInTransaction(TransactionStatus status) {
            // ...
            return null;
        }
    });
}
</code>

2. Prepare Environment

Create a table with 20 million rows and no indexes except the primary key.

3. Simulate Transaction Timeout

Test 1

Count rows inside a transaction with a 20‑second timeout.

<code>// Set timeout to 20s
@Transactional(timeout = 20)
public void query() {
    long start = System.currentTimeMillis();
    jdbcTemplate.execute("select count(*) from p_user");
    System.out.println("耗时:" + (System.currentTimeMillis() - start) + "毫秒");
}
</code>

Result: 3198 ms (within the timeout).

Changing the timeout to 3 seconds triggers a MySQLTimeoutException from the driver:

<code>com.mysql.cj.jdbc.exceptions.MySQLTimeoutException: Statement cancelled due to timeout or client request
    at com.mysql.cj.jdbc.StatementImpl.checkCancelTimeout(StatementImpl.java:2167)
</code>

Test 2

Sleep 3 seconds before the DB operation, timeout set to 2 seconds.

<code>@Transactional(timeout = 2)
public void query() {
    long start = System.currentTimeMillis();
    try {
        TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    jdbcTemplate.execute("select 1");
    System.out.println("耗时:" + (System.currentTimeMillis() - start) + "毫秒");
}
</code>

Result: Spring throws a TransactionTimedOutException.

Test 3

Execute the DB operation first, then sleep 3 seconds (still timeout 2 seconds).

<code>@Transactional(timeout = 2)
public void query() {
    long start = System.currentTimeMillis();
    jdbcTemplate.execute("select 1");
    try {
        TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("耗时:" + (System.currentTimeMillis() - start) + "毫秒");
}
</code>

Result: The method finishes normally (≈3015 ms) because the timeout is checked only before each DB call.

Test 4

Same as Test 3 but perform a second DB call after the sleep.

<code>@Transactional(timeout = 2)
public void query() {
    long start = System.currentTimeMillis();
    jdbcTemplate.execute("select 1");
    try {
        TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("耗时:" + (System.currentTimeMillis() - start) + "毫秒");
    // second DB operation
    jdbcTemplate.execute("select 1");
}
</code>

Result: The first DB call succeeds; the second triggers a TransactionTimedOutException, confirming that the timeout is evaluated at each database interaction.

4. Transaction Timeout Mechanism

When a transaction starts, DataSourceTransactionManager#doBegin sets the timeout:

<code>protected void doBegin(Object transaction, TransactionDefinition definition) {
    int timeout = determineTimeout(definition);
    if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
        txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
    }
}

protected int determineTimeout(TransactionDefinition definition) {
    if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
        return definition.getTimeout();
    }
    return getDefaultTimeout();
}
</code>

When JdbcTemplate creates a Statement , it applies the timeout:

<code>protected void applyStatementSettings(Statement stmt) throws SQLException {
    DataSourceUtils.applyTimeout(stmt, getDataSource(), getQueryTimeout());
}
</code>

DataSourceUtils.applyTimeout chooses the remaining transaction timeout or the explicit value:

<code>public static void applyTimeout(Statement stmt, @Nullable DataSource dataSource, int timeout) throws SQLException {
    ConnectionHolder holder = null;
    if (dataSource != null) {
        holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
    }
    if (holder != null && holder.hasTimeout()) {
        stmt.setQueryTimeout(holder.getTimeToLiveInSeconds());
    } else if (timeout >= 0) {
        stmt.setQueryTimeout(timeout);
    }
}
</code>

ResourceHolderSupport computes the remaining time and throws TransactionTimedOutException when the deadline is reached:

<code>public int getTimeToLiveInSeconds() {
    double diff = ((double) getTimeToLiveInMillis()) / 1000;
    int secs = (int) Math.ceil(diff);
    checkTransactionTimeout(secs <= 0);
    return secs;
}

private void checkTransactionTimeout(boolean deadlineReached) throws TransactionTimedOutException {
    if (deadlineReached) {
        setRollbackOnly();
        throw new TransactionTimedOutException("Transaction timed out: deadline was " + this.deadline);
    }
}
</code>

In summary, the transaction start records the deadline; each database operation checks the elapsed time against this deadline, and if exceeded, Spring rolls back the transaction and throws a timeout exception.

backendjavatransactionDatabaseSpringtimeout
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

login 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.