Below is part of simple @DataJPATest. Two tests are identical.
It rolls back without problem when it executed in a single thread.
@Test
void SingleThreadTest1() throws RequesterException, InterruptedException {
System.out.println("(before)count: " + logRepository.count());
App app = appRepository.findByName("TestApp").orElseThrow();
myService.loggingTransaction(app);
System.out.println("(after)count: " + logRepository.count());
}
@Test
void SingleThreadTest2() throws RequesterException, InterruptedException {
System.out.println("(before)count: " + logRepository.count());
App app = appRepository.findByName("TestApp").orElseThrow();
myService.loggingTransaction(app);
System.out.println("(after)count: " + logRepository.count());
}
Result:
#SingleThreadTest1
(before)count: 0
(after)count: 1
#SingleThreadTest2
(before)count: 0
(after)count: 1
But It doesn't roll back when operating in multi-threaded.
@DataJpaTest
class ServiceTest {
static final int CONCURRENCY = 1;
@Test
void MultiThreadTest1() throws RequesterException, InterruptedException {
System.out.println("(before)count: " + logRepository.count());
App app = appRepository.findByName("TestApp").orElseThrow();
// --------From Here: Spawn sub thread---------
ThreadPoolExecutor executor = new ThreadPoolExecutor(CONCURRENCY, CONCURRENCY, 1, TimeUnit.MINUTES, new SynchronousQueue<>());
executor.prestartAllCoreThreads();
List<Callable<OptoutMessage>> callables = IntStream.range(0, CONCURRENCY)
.<Callable<OptoutMessage>>mapToObj(i -> () -> myService.loggingTransaction(app))
.toList();
List<Future<OptoutMessage>> futures = executor.invokeAll(callables);
executor.shutdownNow();
// ------------------Ultil here----------------
System.out.println("(after)count: " + logRepository.count());
}
@Test
void MultiThreadTest2() throws RequesterException, InterruptedException {
System.out.println("(before)count: " + logRepository.count());
App app = appRepository.findByName("TestApp").orElseThrow();
// --------From Here: Spawn sub thread---------
ThreadPoolExecutor executor = new ThreadPoolExecutor(CONCURRENCY, CONCURRENCY, 1, TimeUnit.MINUTES, new SynchronousQueue<>());
executor.prestartAllCoreThreads();
List<Callable<OptoutMessage>> callables = IntStream.range(0, CONCURRENCY)
.<Callable<OptoutMessage>>mapToObj(i -> () -> myService.loggingTransaction(app))
.toList();
List<Future<OptoutMessage>> futures = executor.invokeAll(callables);
executor.shutdownNow();
// ------------------Ultil here----------------
System.out.println("(after)count: " + logRepository.count());
}
Result:
#MultiThreadTest1
(before)count: 0
(after)count: 1
#MultiThreadTest2
(before)count: 1 // <- IT SHOULD BE 0 !!!
(after)count: 2
Question:
- Why
@DataJPATestdoesn't rolls back when transaction when it occurs in sub thread? - What should I do to roll back the data after
@Testunder this situation?
We recently had a similar issue.
Question 1: I cannot give you a sufficient answer, as I also don't know exactly why it is not rolling back.
Question 2: What worked for us was to add the annotation
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/annotation/DirtiesContext.html