In my Spring Boot project I have implemented following service method:
@Transactional public boolean validateBoard(Board board) { boolean result = false; if (inProgress(board)) { if (!canPlayWithCurrentBoard(board)) { update(board, new Date(), Board.AFK); throw new InvalidStateException(ErrorMessage.BOARD_TIMEOUT_REACHED); } if (!canSelectCards(board)) { update(board, new Date(), Board.COMPLETED); throw new InvalidStateException(ErrorMessage.ALL_BOARD_CARDS_ALREADY_SELECTED); } result = true; } return result; }
Inside this method I use another service method which is called update
:
@Transactional(propagation = Propagation.REQUIRES_NEW) public Board update(Board board, Date finishedDate, Integer status) { board.setStatus(status); board.setFinishedDate(finishedDate); return boardRepository.save(board); }
I need to commit changes to database in update
method independently of the owner transaction which is started in validateBoard
method. Right now any changes is rolling back in case of any exception.
Even with @Transactional(propagation = Propagation.REQUIRES_NEW)
it doesn't work.
How to correctly do this with Spring and allow nested transactions ?
Not all transaction managers support nested transactions. Spring supports this out of the box only with the JDBC DataSourceTransactionManager, which is what we'll cover.
A nested transaction is used to provide a transactional guarantee for a subset of operations performed within the scope of a larger transaction. Doing this allows you to commit and abort the subset of operations independently of the larger transaction.
So when you annotate a method with @Transactional , Spring dynamically creates a proxy that implements the same interface(s) as the class you're annotating. And when clients make calls into your object, the calls are intercepted and the behaviors injected via the proxy mechanism.
This documentation covers your problem - https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-annotations
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional. Also, the proxy must be fully initialized to provide the expected behaviour so you should not rely on this feature in your initialization code, i.e. @PostConstruct.
However, there is an option to switch to AspectJ mode
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With