Spring Integration: Transaction Difficulties Between Two Activators

I have this use case.

First chain:

<int:chain input-channel="inserimentoCanaleActivate" output-channel="inserimentoCanalePreRouting"> <int:service-activator ref="inserimentoCanaleActivator" method="activate" /> </int:chain> 

This is the relative code:

 @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public EventMessage<ModificaOperativitaRapporto> activate(EventMessage<InserimentoCanale> eventMessage) { ... // some Database changes dao.save(myObject); } 

Everything works great.

Then I have another chain:

 <int:chain id="onlineCensimentoClienteChain" input-channel="ONLINE_CENSIMENTO_CLIENTE" output-channel="inserimentoCanaleActivate"> <int:service-activator ref="onlineCensimentoClienteActivator" method="activate" /> <int:splitter expression="payload.getPayload().getCanali()" /> </int:chain> 

And relative activator:

 @Override public EventMessage<CensimentoCliente> activate(EventMessage<CensimentoCliente> eventMessage) { ... // some Database changes dao.save(myObject); } 

The CensimentoCliente payload, as described below, has a List payload of the first chain, so the splitter I breaks into a list and reuses the code of the first chain.

 public interface CensimentoCliente extends Serializable { Collection<? extends InserimentoCanale> getCanali(); void setCanali(Collection<? extends InserimentoCanale> canali); ... } 

But since each activator gets its own definition of a transaction (since the first one can live without the second), I have a case where transactions are split.

The goal is to modify the db of two circuits as part of the same transaction.

Any help?

Regards Massimo

+6
source share
3 answers

This can be done by creating your own channel (or another custom component, but this is the easiest approach), which wraps sending messages as a result of the TransactionTemplate callback:

 public class TransactionalChannel extends AbstractSubscribableChannel { private final MessageDispatcher dispatcher = new UnicastingDispatcher(); private final TransactionTemplate transactionTemplate; TransactionalChannel(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } @Override protected boolean doSend(final Message<?> message, long timeout) { return transactionTemplate.execute(new TransactionCallback<Boolean>() { @Override public Boolean doInTransaction(TransactionStatus status) { return getDispatcher().dispatch(message); } }); } @Override protected MessageDispatcher getDispatcher() { return dispatcher; } } 

In your XML, you can define your channel and transaction template and specify your own channel just like any other channel:

  <bean id="transactionalChannel" class="com.stackoverflow.TransactionalChannel"> <constructor-arg> <bean class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"/> <property name="propagationBehavior" value="#{T(org.springframework.transaction.TransactionDefinition).PROPAGATION_REQUIRES_NEW}"/> </bean> </constructor-arg> </bean> 

In your example, you could use a bridge to send a message through a new channel:

 <int:bridge input-channel="inserimentoCanaleActivate" output-channel="transactionalChannel" /> <int:chain input-channel="transactionalChannel" output-channel="inserimentoCanalePreRouting"> <int:service-activator ref="inserimentoCanaleActivator" method="activate" /> </int:chain> 
+3
source

You have <service-activator> and @Transactional in the service method, the transaction will be limited only by calling this method. If you want to have translation for the entire message stream (or part of it), you must first declare a TX tip. If your channels are direct, all service calls will be wrapped in a single transaction. The easiest way to fulfill your wishes is to write a simple @Gateway interface with @Transactional and call it from the very beginning of the message flow.

To talk a little about transactions Understanding transactions in message flows

+2
source

Are these modifications 2 separate relational databases? If so, you are looking at transaction XA. Now, if you use this in a non XA container, such as tomcat, all of this should be done in a single thread, which is viewed by the transaction manager - (you will have to return to the transaction manager, which actually fires these events). The transaction manager may be a JMS message or a controller for some data source. Also, this processing should be performed in one thread, so spring can help you start the whole process in one transaction.

As a final note, do not enter thread / queue flows between service activators. This can cause activators to run in separate threads.

0
source

Source: https://habr.com/ru/post/896987/


All Articles