Reputation: 531
I have to save data to 2 different tables. I need it be all-or-none transaction. How do I rollback first transaction if the second transaction fails?
In the below code, I storeToTable1(), storeToTable2() is executed in that order.
I want storeToTable1() to rollback if storeToTable2() fails.
Config class:
class StoreToDBConfig {
@Bean
public IntegrationFlow receiveMessage() {
return IntegrationFlows
.from("inputChannel")
.transform("convertToTable1Format")
.handle(ServicetoDBAdaptor,"storeToTable1")
.transform("convertToTable2Format")
.handle(ServicetoDBAdaptor,"storeToTable2")
.get();
}
}
Service to DB Adaptor:
class ServicetoDBAdaptor {
@Autowired
UserExecutionDetail userExecutionDetail;
@Autowired
UserExecution userExecution;
@Autowired
UserExecutionDetailRepository userExecutionDetailRepository;
@Autowired
UserExecutionRepository userExecutionRepository;
@Transactional(propagation=Propagation.REQUIRED)
@ServiceActivator
private void storeToTable1(Message<UserExecutionDetail> msg) throws Exception {
userExecutionDetailRepository.save(msg.getPayload());
}
@Transactional(propagation=Propagation.REQUIRED)
@ServiceActivator
private void storeToTable2(Message<UserExecution> msg) throws Exception {
userExecutionRepository.save(msg.getPayload());
}
}
Upvotes: 1
Views: 359
Reputation: 121177
For this purpose you need to use on the first handle()
something like this:
.handle(ServicetoDBAdaptor,"storeToTable1", e -> e.transactional(true))
This hook will do exactly this:
/**
* Specify a {@link TransactionInterceptor} {@link Advice} with default
* {@code PlatformTransactionManager} and {@link DefaultTransactionAttribute} for the
* {@link MessageHandler}.
* @param handleMessageAdvice the flag to indicate the target {@link Advice} type:
* {@code false} - regular {@link TransactionInterceptor}; {@code true} -
* {@link org.springframework.integration.transaction.TransactionHandleMessageAdvice}
* extension.
* @return the spec.
*/
public S transactional(boolean handleMessageAdvice) {
Where we are going to use this one:
/**
* A {@link TransactionInterceptor} extension with {@link HandleMessageAdvice} marker.
* <p>
* When this {@link Advice} is used from the {@code request-handler-advice-chain}, it is applied
* to the {@link MessageHandler#handleMessage}
* (not to the
* {@link org.springframework.integration.handler.AbstractReplyProducingMessageHandler.RequestHandler#handleRequestMessage}),
* therefore the entire downstream process is wrapped to the transaction.
* <p>
* In any other cases it is operated as a regular {@link TransactionInterceptor}.
*
* @author Artem Bilan
*
* @since 5.0
*/
@SuppressWarnings("serial")
public class TransactionHandleMessageAdvice extends TransactionInterceptor implements HandleMessageAdvice {
The key trick here is the entire downstream process is wrapped to the transaction.
.
Therefore a transaction is going to start from your storeToTable1()
and will be widened until the end of the flow and your storeToTable2()
is going to participate in the same TX. So, when that one is rolled back, the first one will be rolled back as well. Just because you will have only single transaction at all!
Upvotes: 2