Backend Development 4 min read

Using @Transactional and @Async Together in Spring: Interaction, Transaction Propagation, and Best Practices

This article explains how Spring's @Transactional and @Async annotations can be applied to the same service methods, the implications for transaction boundaries across threads, and the precautions developers must take to maintain data consistency and proper transaction management.

Cognitive Technology Team
Cognitive Technology Team
Cognitive Technology Team
Using @Transactional and @Async Together in Spring: Interaction, Transaction Propagation, and Best Practices

The previous post introduced the relationship between @Transactional and @Async when both annotate the same method; this article now examines whether they can be used together and how to manage their interaction correctly.

@Transactional is a Spring annotation that declares a method or class requires transaction support, creating a new transaction or joining an existing one based on its propagation settings.

@Async is a Spring annotation that executes the annotated method asynchronously in a separate thread.

When combining @Transactional and @Async, developers must ensure transaction boundaries are correctly managed because @Async runs in a different thread, which can lead to issues if transaction propagation is not properly configured.

Example of calling a transactional method from an async method:

@Async
public void transferAsync(Long depositorId, Long favoredId, BigDecimal amount) {
    transfer(depositorId, favoredId, amount);
    // other async operations, isolated from transfer
}

Transactional method implementation:

@Transactional
public void transfer(Long depositorId, Long favoredId, BigDecimal amount) {
    Account depositorAccount = accountRepository.findById(depositorId)
        .orElseThrow(IllegalArgumentException::new);
    Account favoredAccount = accountRepository.findById(favoredId)
        .orElseThrow(IllegalArgumentException::new);
    depositorAccount.setBalance(depositorAccount.getBalance().subtract(amount));
    favoredAccount.setBalance(favoredAccount.getBalance().add(amount));
    accountRepository.save(depositorAccount);
    accountRepository.save(favoredAccount);
}

Calling a @Transactional method from an @Async method can improve performance by allowing parallel execution, but developers must be aware that Spring uses ThreadLocal to manage the current transaction, so the transaction context is not shared across threads.

Consequently, when a @Transactional method invokes an @Async method, the async execution runs with its own transaction context, and no thread‑local transaction data is propagated from the caller.

To avoid data‑consistency problems, it is recommended to avoid invoking @Async methods from within @Transactional methods unless explicit transaction propagation strategies or additional handling are applied.

In summary, @Transactional and @Async annotated methods can call each other, but developers must understand and correctly handle the associated complexities and potential issues.

JavaSpringthreadlocalasyncTransactionalTransaction Management
Cognitive Technology Team
Written by

Cognitive Technology Team

Cognitive Technology Team regularly delivers the latest IT news, original content, programming tutorials and experience sharing, with daily perks awaiting you.

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.