'@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