我已将同步过程转移到异步,现在我有一些麻烦来维护集成测试。这似乎与以下事实相关:当您在@Transactional方法中创建新线程,然后调用新的@Transactional时,Spring会创建一个新事务。
在集成测试期间,@ Transnsaction测试会出现问题。由于测试配置中的TransactionalTestExecutionListener,似乎在测试完成之前回滚了线程事务。
我已经尝试了很多类似的东西 - 自动装配EntityManager并在线程完成后手动刷新 - 在测试方法中使用@Rollback而不是@Transactional - 使用TestTransaction管理事务 - 一起使用@Rollback和TestTransaction以下是简化的源代码:
public interface MyService{
public void doThing(someArgs...);
public void updateThings(someArgs...);
}
@Service
public class MyServiceImpl implements MyService{
@Autowired
private AsynchronousFutureHandlerService futureService;
@Autowired
@Qualifier("myExecutorService")
private ScheduledExecutorService myExecutorService;
@Transactional
@Override
public void doThing(someArgs...){
doThingAsync(someArgs...);
}
private void doThingAsync(someArgs...){
AsynchronousHandler runnable = applicationContext.getBean(
AsynchronousHandler.class, someArgs...);
//as we are executing some treatment in a new Thread, a new transaction is automatically created
Future<?> future = myExecutorService.submit(runnable);
//keep track of thread execution
futureService.addFutures(future);
}
@Override
@Transactional
public void updateThings(someArgs...){
//do udpate stuff
}
}
/**
* very basic solution to improve later to consult thread state
*/
@Service
public class AsynchronousFutureHandlerService {
//TODO : pass to ThreadSafe collection
private List<Future<?>> futures = new ArrayList<>();
public void addTheoreticalApplicationFuture(Future<?> future){
futures.add(future);
this.deleteJobsDone();
}
public boolean isThreadStillRunning(){
boolean stillRunning = false;
for(Future<?> f : futures){
if(!f.isDone()){
stillRunning = true;
break;
}
}
return stillRunning;
}
public void deleteJobsDone(){
this.futures.removeIf(f -> f.isDone());
}
}
@Component
@Scope("prototype")
public class AsynchronousHandler implements Runnable {
@Autowired
private MyService myService;
@Override
public void run() {
myService.updateThings(...); //updates data in DB
...
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfiguration.class)
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DataSetTestExecutionListener.class,
TransactionalTestExecutionListener.class })
@DataSet(dbType = DBType.H2, locations = { "classpath:dataset.xml" })
public class MyServiceTest{
@Autowired
private MyService myService;
@Autowired
private AsynchronousFutureHandlerService futureService;
@Test
@Transactional
public void test_doThings(){
myService.doThings(someArgs...);
waitUpdateFinish();
Assert.assertEquals(...); //fails here because Thread transaction has been rollbacked
}
private void waitUpdateFinish() throws InterruptedException{
while(futureService.isThreadStillRunning()){
Thread.sleep(500);
}
}
}