简体   繁体   中英

@Transactional on @Async methods in Spring

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.

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.

I think that yours @Async method can throw checked exception

@Transactional
@Async
public Future<Void> execute(BillingRequestDto requestDto) throw RollBackParentTransactionException {
...
}

and the caller can be annotated with:

@Transactional(rollbackFor = RollBackParentTransactionException.class)
public void execute(BillingRequestDto requestDto) { ... }

that should work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM