开发者

Spring integration: difficulty with transaction between 2 activators

开发者 https://www.devze.com 2023-04-04 06:37 出处:网络
I have this use case. First chain: <int:chain input-channel=\"inserimentoCanaleActivate\" output-channel=\"inserimentoCanalePreRouting\">

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);
}

All is working 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 the 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 of payload of the first chain, so with a splitter I split on the list and reuse the code of the first chain.

public interface CensimentoCliente extends Serializable {

    Collection<? extends InserimentoCanale> getCanali();

    void setCanali(Collection<? extends InserimentoCanale> canali);
    ...
}

But since every activator gets his transaction definition (since the first one can live without the second one) I have a use case where the transactions are separated.

The goal is to have the db modifies of the two chains been part of the same tra开发者_如何学运维nsaction.

Any help?

Kind regards Massimo


You can accomplish this by creating a custom channel (or other custom component, but this is the simplest approach) that wraps the message dispatch in a TransactionTemplate callback execution:

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 reference your custom channel just as you would 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>

For your example, you could perhaps use a bridge to pass the message through the 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>


You you have <service-activator> and @Transactional on service method, the transaction will be bounded only to that method invocation. If you want to have a transction for entire message flow (or its part) you should declare TX advice somewhere before. If your channels are direct all service invocations will be wrapped with the same transaction. The most simple way to accomplish your wishes, write simple @Gateway interface with @Transactional and call it from the start of your message flow.

To clarify a bit regarding transactions Understanding Transactions in Message flows


Are these modifying 2 separate relational databases ? If so you are looking at an XA transaction. Now if you are running this on a non XA container like tomcat, all of this must be done in a single thread that is watched by a transaction manager - (you will have to piggy back on the transaction manager that actually triggers these events). The transaction manager can be a JMS message or a poller against some data source. Also this processing must be done in a single thread so that spring can help you run the entire process in a single transaction.

As a final note , do not introduce threadpools / queues between service activators. This can cause the activators to run in separate threads

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号