Spring集成:2个激活器之间的事务处理困难

时间:2011-09-09 13:32:15

标签: spring transactions spring-integration

我有这个用例。

第一链:

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

这是相关代码:

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

一切都很好。

然后我有另一个链:

<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>

相对激活者:

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

如下所述的CensimentoCliente有效负载的第一个链的有效负载为List,因此使用拆分器,我会在列表中拆分并重用第一个链的代码。

public interface CensimentoCliente extends Serializable {

    Collection<? extends InserimentoCanale> getCanali();

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

但是因为每个激活器都获得了他的事务定义(因为第一个激活器可以在没有第二个激活器的情况下生存),所以我有一个用例,其中事务是分开的。

目标是让两个链的数据库修改成为同一事务的一部分。

任何帮助?

亲切的问候 马西莫

3 个答案:

答案 0 :(得分:3)

您可以通过创建自定义通道(或其他自定义组件,但这是最简单的方法)来完成此操作,该通道将消息调度包装在TransactionTemplate回调执行中:

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

}

在您的XML中,您可以像定义任何其他频道一样定义频道和交易模板并引用自定义频道:

    <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>

对于您的示例,您可以使用网桥通过新频道传递消息:

<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>

答案 1 :(得分:2)

您对服务方法有<service-activator>@Transactional,该事务将仅限于该方法调用。 如果您希望对整个消息流(或其部分)进行转换,则应在之前的某处声明TX建议。 如果您的频道是直接的,则所有服务调用都将使用相同的事务进行包装。 实现您的愿望的最简单方法是,使用@Gateway编写简单的@Transactional界面,并从消息流的开头调用它。

澄清有关交易的一点 Understanding Transactions in Message flows

答案 2 :(得分:0)

这些是修改2个独立的关系数据库吗?如果是这样,您正在查看XA事务。现在如果你在像tomcat这样的非XA容器上运行它,所有这一切都必须在一个事务管理器监视的单个线程中完成 - (你将不得不捎带实际触发这些事件的事务管理器)。事务管理器可以是JMS消息或针对某些数据源的轮询器。此处理也必须在单个线程中完成,以便spring可以帮助您在单个事务中运行整个过程。

最后请注意,不要在服务激活器之间引入线程池/队列。这可能导致激活器在单独的线程中运行