@Transactional on @Async methods in Spring

14,367

I guess you should use spring TransactionTemplate for programmatic control.
The main thread should perform control, if any thread throws exception you should notify the others thread that they should be rollbacked.

Say, each "transactional" thread after execution should wait(), in case of no exception just perform notifyAll() and in your thread do transaction commit, in case of exception you should call Thread.interrupt() and do rollback.

Share:
14,367
Pramod
Author by

Pramod

I am a mathematician by nature and Technologist by profession. Having 11 years of technology experience. Husband of a beautiful Wife Father of a cute son. Lives in India

Updated on June 18, 2022

Comments

  • Pramod
    Pramod almost 2 years

    I have a scenario where I call three @Transactional @Async methods. Everything works fine except all three methods have their own transaction context. I want to execute them in calling method's transaction context.

    My Calling method is like this:

     @Transactional
     public void execute(BillingRequestDto requestDto) {
            try {
                LOGGER.info("Start Processing Request : {}", requestDto.getId());
                List<Future<?>> futures = new ArrayList<>();
                futures.add(inboundProcessingService.execute(requestDto));
                futures.add(orderProcessingService.execute(requestDto));
                futures.add(waybillProcessingService.execute(requestDto));
                futures.stream().parallel().forEach(future -> {
                    try {
                        future.get();
                    } catch (Exception e) {
                        futures.forEach(future1 -> future1.cancel(true));
                        throw new FBMException(e);
                    }
                });
                requestDto.setStatus(RequestStatus.SUCCESS.name());
                requestDto.setCompletedAt(new Date());   
                LOGGER.info("Done Processing Request : {}", requestDto.getId());
    
            } catch (Exception e) {
                requestDto.setStatus(RequestStatus.FAIL.name());
                requestDto.setCompletedAt(new Date());
                throw new FBMException(e);
            } 
        }
    

    And all called methods are annotated with @Async and @Transactional.

    @Transactional
    @Async
    public Future<Void> execute(BillingRequestDto requestDto) {
        LOGGER.info("Start Waybill Processing {}", requestDto.getId());
        long count = waybillRepository.deleteByClientNameAndMonth(requestDto.getClientName(), requestDto.getMonth());
        LOGGER.info("Deleted  {} Records for Request {} ", count, requestDto.getId());
        try (InputStream inputStream = loadCsvAsInputStream(requestDto)) {
            startBilling(requestDto, inputStream);
        } catch (IOException e) {
            LOGGER.error("Error while processing");
            throw new FBMException(e);
        }
        LOGGER.info("Done Waybill Processing {}", requestDto.getId());
        return null;
    }
    

    Implementation for all three methods is more or less same.

    Now if there is a failure in any of these methods then transaction in rolled-back for that method only.

    My requirement is to run all three methods in calling methods transaction context so any exception in one method will rollback all three methods.

    This scenario works well if I disable @Async. There are time taking methods so I want them to run in parallel.

    Please suggest any solution for this.