'@Transactional on @Async methods in Spring Boot with shared object
I have a requirement where I have to save a lot of data into an Oracle DB.
I want to use multithreading to speed things up. Everything is in a transaction.
I have two tables in a many to one relation.
First, I synchronously save the one-side entity with repo.save()
Next I build batches of 25 many-side entity entries (from an initial list of a few hundred) and start a new thread using @Async to save them. Each of this entities has to reference the one-side entity so I pass the new one-side entity created previously to the @Async methods. I wait for all of them to complete before exiting.
Problem is I get the following exception :
java.sql.BatchUpdateException: ORA-02291: integrity constraint (USER.ENTITY_FK1) violated - parent key not found
Which basically means that the one-side entity I am trying to reference from the many-side does not exist.
From here the transaction fails and everything gets rolled back.
Can someone help me ?
Thanks
Update with code :
In OrchestratorService.class :
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void importDataFromFile(MultipartFile file) throws Exception {
MyDto myDto = convertor.convertFileToDto(file);
log.info("myDto : {}", myDto.toString());
ParentEntity savedParent = parentEntityService.saveParent(myDto.getFileName());
childService.saveAllChildren(savedParent, myDto);
}
ParentEntityService.class :
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public ParentEntity saveParent(String fileName){
ParentEntity parentEntity= ParentEntity.builder()
.fileName(fileName)
.loadingDate(LocalDate.now())
.build();
ParentEntity savedParentEntity = parentEntityRepository.save(parentEntity);
log.info("Saved ParentEntity : {} ", savedParentEntity );
return savedPpassImportCtrlEntity;
}
ChildService.class :
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void saveAllChildren(ParentEntity parentEntity, MyDto myDto) throws Exception{
List<CompletableFuture<ProcessStatus>> completableFutures = new ArrayList<>();
List<List<ChildrenData>> lists = ListUtils.partition(myDto.getChildrenDataList(), 25);
for (List<ChildrenData> list :lists) {
CompletableFuture<ProcessStatus> result = asyncSaveBatchService.saveBatch(parentEntity, list);
completableFutures.add(result);
}
// wait for all to finish
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0]));
combinedFuture.get();
for(CompletableFuture<ProcessStatus> result : completableFutures){
if(result.get().getStatus().equals(Boolean.FALSE)){
throw new RuntimeException("Process failed : \n " + result.get().getErrorMessage());
}
}
}
And finally AsyncSaveBatchService.class :
@Async("threadPoolTaskExecutor")
public CompletableFuture<ProcessStatus> saveBatch(ParentEntity parentEntity, List<ChildrenData> list){
try{
List<ChildrenEntity> childrenEntitiesToSave = new ArrayList<>();
for (ChildrenData childrenData: list) {
ChildrenEntity childrenEntity = ChildrenEntity .builder()
.firstName(childrenData.getFirstName())
.lastName(childrenData.getLastName())
.parent(parentEntity)
.build();
childrenEntitiesToSave.add(childrenEntity);
}
childrenEntityRepository.saveAll(childrenEntitiesToSave);
return CompletableFuture.completedFuture(ProcessStatus.builder().status(Boolean.TRUE).errorMessage(null).build());
}catch (Exception ex){
return CompletableFuture.completedFuture(ProcessStatus.builder().status(Boolean.FALSE).errorMessage(ExceptionUtils.getStackTrace(ex)).build());
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|