Why @Transactional Fails with parallelStream and How to Fix It
An Excel import that partially succeeds reveals a hidden bug where Spring’s @Transactional annotation doesn’t roll back when using Java 8’s parallelStream, and the article explains the underlying thread‑local transaction mechanics, demonstrates the issue with code, and offers practical solutions and best‑practice guidelines.
Transaction Not Effective Code
The original method uses @Transactional(rollbackFor = Exception.class) and processes a list of Order objects with
list.parallelStream().forEach(order -> orderMapper.save(order)). The expectation is that any exception during the batch insert will trigger a rollback, but in practice the transaction does not roll back when a single record fails.
@Transactional(rollbackFor = Exception.class)
public void batchInsert(List<Order> list) {
list.parallelStream().forEach(order -> orderMapper.save(order));
}JDK 8 Stream
Java 8 introduced the Stream API, which operates on object streams rather than byte streams. A simple sequential stream example:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
numbers.stream().forEach(num -> System.out.println(num));
// Output: 1 2 3 4 5 6 7 8 9Parallel processing is provided via parallelStream(), which splits the workload into sub‑tasks and executes them concurrently using the common ForkJoinPool. This can improve performance when the data set is large and the machine has multiple CPU cores.
Typical usage of parallelStream:
// Parallel execution
list.stream().parallel().filter(e -> e > 10).count();@Transactional Transaction Handling
Spring’s @Transactional is a declarative transaction mechanism that works only on public methods. When a method is annotated, Spring creates a proxy that opens a transaction before method execution and either commits or rolls back based on whether an exception is thrown.
In the default proxy mode, the transaction interceptor only applies when the method is invoked from outside the target class. Direct internal calls bypass the proxy, which is a common cause of transactions not being applied, though this is not the case in the current example.
Spring obtains a JDBC connection from the connection pool and binds it to the current thread via ThreadLocal. All database operations performed on that thread share the same connection, as implemented in DataSourceTransactionManager#doBegin.
Bug Comprehensive Analysis
The root cause is that parallelStream executes the stream pipeline in multiple threads. Since Spring binds the JDBC connection to the thread that started the transaction, the worker threads created by the parallel stream do not share that connection. Consequently, when an exception occurs in a worker thread, the transaction manager does not see it and cannot roll back.
Replacing parallelStream() with a regular stream() (or processing the list sequentially) restores the expected rollback behavior. The lesson is to avoid mixing Spring’s thread‑local transaction management with parallel execution unless the transaction context is explicitly propagated.
Problem Extension
Even when transaction management is not required, careless use of parallelStream can lead to other issues such as excessive concurrent database connections, race conditions, and nondeterministic execution order. Before choosing between stream and parallelStream, consider:
Is parallelism needed? Significant performance gains appear only with large data volumes and multiple CPU cores.
Are tasks independent? Shared mutable state can cause race conditions.
Does the result depend on processing order? Parallel execution does not guarantee order.
Summary
The article demonstrates a subtle bug where Spring’s @Transactional does not roll back when a parallelStream processes database inserts. Understanding the thread‑local nature of Spring transactions and the concurrency model of Java streams is essential for diagnosing such issues and for making informed decisions about when to employ parallel processing.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Senior Brother's Insights
A public account focused on workplace, career growth, team management, and self-improvement. The author is the writer of books including 'SpringBoot Technology Insider' and 'Drools 8 Rule Engine: Core Technology and Practice'.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
