Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keep transaction within Spring Integration flow

Inboud gateway:

<int-http:inbound-gateway   id="inbound.gateway"
                            request-channel="transactional.channel.input"
                            reply-channel="channel.output"
                            error-channel="channel.error"
                            request-payload-type="java.lang.String"
</int-http:inbound-gateway>

Advice definition:

<tx:advice id="advice">
    <tx:attributes>
        <tx:method name="send" propagation="REQUIRES_NEW" rollback-for="MyClassException"/>
    </tx:attributes>
</tx:advice>

Advice config:

<aop:config>
    <aop:advisor advice-ref="advice" pointcut="bean(transactional.channel.input)"/>
</aop:config>

Chain that needs to be transactional:

<int:chain input-channel="transactional.channel.input" output-channel="non.transactional.channel.input>
    <int:service-activator ref="v1.registerUser.service" method="registerUser"/>
    <int:service-activator ref="v1.saveObject.service" method="saveObject"/>
</int:chain>

Chain that needs the transaction to be executed previously to get the object id generated in last transational chain step:

<int:chain input-channel="non.transactional.channel.input" output-channel="channel.output">
    <int:service-activator  ref="v1.getObjectId.service" method="getObjectId"/>
    <int:object-to-json-transformer/>
</int:chain>

Having this simplified context, when I access to the id in getObjectId service, the transaction has not been executed.

So the transaction seems to be commited at inbound-gateway output level.

like image 796
Antonio Acevedo Avatar asked Sep 19 '13 17:09

Antonio Acevedo


2 Answers

One more amazing trick without writing any Java code:

<channel id="input"/>

<aop:config>
    <aop:advisor advice-ref="txAdvice" pointcut="bean(input)"/>
</aop:config>

<tx:advice id="txAdvice">
    <tx:attributes>
     <tx:method name="send"/>
    </tx:attributes>
</tx:advice>

With this all your direct singlethreaded message flow will be wrapped to the TX on the message sending to the channel input

like image 168
Artem Bilan Avatar answered Nov 05 '22 15:11

Artem Bilan


If it's a MessagingGateway injected into your code, you can simply start your transaction at the gateway and, since all channels are direct, the entire flow will run in the same transaction. Simply annotate your gateway method with @Transactional and add <tx:annotation-driven/> or @EnableTransactionManagement to your context (and a transaction manager).

Or you can start your transaction even earlier if you want other stuff in the transaction...

@Transactional
public void foo() {
    ...
    Object reply = myGateway.exchange(Object foo);
    ...
}

Just be sure to invoke foo() from another bean so that the class containing foo() is wrapped in a transaction proxy (by @EnableTransactionManagement or <tx:annotation-driven/>).

If it's a gateway such as an http inbound gateway, add an @Transactional gateway after the inbound gateway to start the transaction. (Add a service-activator that refs a <gateway/> that exchanges Message<?> and is annotated with @Transactional).

like image 45
Gary Russell Avatar answered Nov 05 '22 17:11

Gary Russell