http://camel.apache.org/transactional-client.html

https://github.com/gitchina/jpaJxet-Atomikos-Manage

该项目为公司内部研发采用二个数据源来完成分布式事务的操作采用第三方开源的JTA实现http://www.atomikos.com/Documentation.
采用Maven构建

Hi!



Using transactions inside OSGI can be tricky. First, you need global transaction manager. If you plan to use same TX manager in multiple bundles (not your case), you need additional bundle which will expose TX as bean reference, ie:





<bean id="transactionManagerProvider"

class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init"

destroy-method="close">

<property name="forceShutdown" value="true"></property>

</bean>



<osgi:service id="transactionManager" interface="javax.transaction.TransactionManager" ref="transactionManagerProvider"/>



<bean id="userTransactionProvider" class="com.atomikos.icatch.jta.UserTransactionImp"/>



<osgi:service id="userTransaction" interface="javax.transaction.UserTransaction" ref="userTransactionProvider"/>





And in your bundle(s) you then need:





<tx:annotation-driven transaction-manager="txManager"/>



<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">

<property name="transactionManager" ref="TransactionManagerServer"></property>

<property name="userTransaction" ref="UserTransactionServer"></property>

</bean>



<osgi:reference id="TransactionManagerServer" interface="javax.transaction.TransactionManager"/>



<osgi:reference id="UserTransactionServer" interface="javax.transaction.UserTransaction"/>



Only after this, all your bundles will be aware of global transaction manager - otherwise, i guess that thing will broke.



And additional small note - when you annotate class (or method) to be transactional, entering this class/mehod will start transaction, and leaving it will commit it, regardless of previous or later calls. I guess that your first call commit-s it. Additonally, check if runtime exception will actually do rollback - there is option on tx annotation to specify on which exceptions to rollback, and on which to ignore.

I found how to solve my issue by excluding the Camel packages in my pointcut expression:



<aop:pointcut id="allServiceMethods" expression="execution(* com.mycompany..*.*(..)) and !within(org.apache.camel..*)"/>

Camel recommends supporting the Transactional Client from the EIP patterns using spring transactions.

Transaction Oriented Endpoints (Camel Toes) like JMS support using a transaction for both inbound and outbound message exchanges. Endpoints that support transactions will participate in the current transaction context that they are called from.

Configuration of Redelivery
The redelivery in transacted mode is not handled by Camel but by the backing system (the transaction manager). In such cases you should resort to the backing system how to configure the redelivery.

You should use the SpringRouteBuilder to setup the routes since you will need to setup the spring context with the TransactionTemplates that will define the transaction manager configuration and policies.

For inbound endpoint to be transacted, they normally need to be configured to use a Spring PlatformTransactionManager. In the case of the JMS component, this can be done by looking it up in the spring context.

You first define needed object in the spring configuration.

< bean id = "jmsTransactionManager" class = "org.springframework.jms.connection.JmsTransactionManager" >
   < property name = "connectionFactory" ref = "jmsConnectionFactory" />
</ bean >
< bean id = "jmsConnectionFactory" class = "org.apache.activemq.ActiveMQConnectionFactory" >
   < property name = "brokerURL" value = "tcp://localhost:61616" />
</ bean >

Then you look them up and use them to create the JmsComponent.

PlatformTransactionManager transactionManager = (PlatformTransactionManager) spring.getBean( "jmsTransactionManager" );
ConnectionFactory connectionFactory = (ConnectionFactory) spring.getBean( "jmsConnectionFactory" );
JmsComponent component = JmsComponent.jmsComponentTransacted(connectionFactory, transactionManager);
component.getConfiguration().setConcurrentConsumers( 1 );
ctx.addComponent( "activemq" , component);

Transaction Policies

Outbound endpoints will automatically enlist in the current transaction context. But what if you do not want your outbound endpoint to enlist in the same transaction as your inbound endpoint? The solution is to add a Transaction Policy to the processing route. You first have to define transaction policies that you will be using. The policies use a spring TransactionTemplate under the covers for declaring the transaction demarcation to use. So you will need to add something like the following to your spring xml:

< bean id = "PROPAGATION_REQUIRED" class = "org.apache.camel.spring.spi.SpringTransactionPolicy" >
   < property name = "transactionManager" ref = "jmsTransactionManager" />
</ bean >
< bean id = "PROPAGATION_REQUIRES_NEW" class = "org.apache.camel.spring.spi.SpringTransactionPolicy" >
   < property name = "transactionManager" ref = "jmsTransactionManager" />
   < property name = "propagationBehaviorName" value = "PROPAGATION_REQUIRES_NEW" />
</ bean >

Then in your SpringRouteBuilder, you just need to create new SpringTransactionPolicy objects for each of the templates.

public void configure() {
    ...
    Policy requried = bean(SpringTransactionPolicy. class , "PROPAGATION_REQUIRED" ));
    Policy requirenew = bean(SpringTransactionPolicy. class , "PROPAGATION_REQUIRES_NEW" ));
    ...
}

Once created, you can use the Policy objects in your processing routes:

// Send to bar in a new transaction
from( "activemq:queue:foo" ).policy(requirenew).to( "activemq:queue:bar" );
// Send to bar without a transaction.
from( "activemq:queue:foo" ).policy(notsupported ).to( "activemq:queue:bar" );

OSGi Blueprint

If you are using OSGi Blueprint then you most likely have to explicit declare a policy and refer to the policy from the transacted in the route.

< bean id = "required" class = "org.apache.camel.spring.spi.SpringTransactionPolicy" >
   < property name = "transactionManager" ref = "jmsTransactionManager" />
   < property name = "propagationBehaviorName" value = "PROPAGATION_REQUIRED" />
</ bean >

And then refer to "required" from the route:

< route >
   < from uri = "activemq:queue:foo" />
   < transacted ref = "required" />
   < to uri = "activemq:queue:bar" />
</ route >

Database Sample

In this sample we want to ensure that two endpoints is under transaction control. These two endpoints inserts data into a database.
The sample is in its full as a unit test.

First of all we setup the usual spring stuff in its configuration file. Here we have defined a DataSource to the HSQLDB and a most importantly
the Spring DataSoruce TransactionManager that is doing the heavy lifting of ensuring our transactional policies. You are of course free to use any
of the Spring based TransactionMananger, eg. if you are in a full blown J2EE container you could use JTA or the WebLogic or WebSphere specific managers.

As we use the new convention over configuration we do not need to configure a transaction policy bean, so we do not have anyPROPAGATION_REQUIRED beans.
All the beans needed to be configured is standard Spring beans only, eg. there are no Camel specific configuration at all.

<!-- this example uses JDBC so we define a data source -->
< jdbc:embedded-database id = "dataSource" type = "DERBY" >
     < jdbc:script location = "classpath:sql/init.sql" />
</ jdbc:embedded-database >
<!-- spring transaction manager -->
<!-- this is the transaction manager Camel will use for transacted routes -->
< bean id = "txManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager" >
     < property name = "dataSource" ref = "dataSource" />
</ bean >
<!-- bean for book business logic -->
< bean id = "bookService" class = "org.apache.camel.spring.interceptor.BookService" >
     < property name = "dataSource" ref = "dataSource" />
</ bean >

Then we are ready to define our Camel routes. We have two routes: 1 for success conditions, and 1 for a forced rollback condition.
This is after all based on a unit test. Notice that we mark each route as transacted using the transacted tag.

< camelContext xmlns = "http://camel.apache.org/schema/spring" >
     < route >
         < from uri = "direct:okay" />
         <!-- we mark this route as transacted. Camel will lookup the spring transaction manager
              and use it by default. We can optimally pass in arguments to specify a policy to use
              that is configured with a spring transaction manager of choice. However Camel supports
              convention over configuration as we can just use the defaults out of the box and Camel
              that suites in most situations -->
         < transacted />
         < setBody >
             < constant >Tiger in Action</ constant >
         </ setBody >
         < bean ref = "bookService" />
         < setBody >
             < constant >Elephant in Action</ constant >
         </ setBody >
         < bean ref = "bookService" />
     </ route >
     < route >
         < from uri = "direct:fail" />
         <!-- we mark this route as transacted. See comments above. -->
         < transacted />
         < setBody >
             < constant >Tiger in Action</ constant >
         </ setBody >
         < bean ref = "bookService" />
         < setBody >
             < constant >Donkey in Action</ constant >
         </ setBody >
         < bean ref = "bookService" />
     </ route >
</ camelContext >

That is all that is needed to configure a Camel route as being transacted. Just remember to use the transacted DSL. The rest is standard Spring XML to setup the transaction manager.

JMS Sample

In this sample we want to listen for messages on a queue and process the messages with our business logic java code and send them along. Since its based on a unit test the destination is a mock endpoint.

First we configure the standard Spring XML to declare a JMS connection factory, a JMS transaction manager and our ActiveMQ component that we use in our routing.

<!-- setup JMS connection factory -->
< bean id = "poolConnectionFactory" class = "org.apache.activemq.pool.PooledConnectionFactory" >
     < property name = "maxConnections" value = "8" />
     < property name = "connectionFactory" ref = "jmsConnectionFactory" />
</ bean >
< bean id = "jmsConnectionFactory" class = "org.apache.activemq.ActiveMQConnectionFactory" >
     < property name = "brokerURL" value = "vm://localhost?broker.persistent=false&amp;broker.useJmx=false" />
</ bean >
<!-- setup spring jms TX manager -->
< bean id = "jmsTransactionManager" class = "org.springframework.jms.connection.JmsTransactionManager" >
     < property name = "connectionFactory" ref = "poolConnectionFactory" />
</ bean >
<!-- define our activemq component -->
< bean id = "activemq" class = "org.apache.activemq.camel.component.ActiveMQComponent" >
     < property name = "connectionFactory" ref = "poolConnectionFactory" />
     <!-- define the jms consumer/producer as transacted -->
     < property name = "transacted" value = "true" />
     <!-- setup the transaction manager to use -->
     <!-- if not provided then Camel will automatic use a JmsTransactionManager, however if you
          for instance use a JTA transaction manager then you must configure it -->
     < property name = "transactionManager" ref = "jmsTransactionManager" />
</ bean >

And then we configure our routes. Notice that all we have to do is mark the route as transacted using the transacted tag.

< camelContext xmlns = "http://camel.apache.org/schema/spring" >
     <!-- disable JMX during testing -->
     < jmxAgent id = "agent" disabled = "true" />
     < route >
         <!-- 1: from the jms queue -->
         < from uri = "activemq:queue:okay" />
         <!-- 2: mark this route as transacted -->
         < transacted />
         <!-- 3: call our business logic that is myProcessor -->
         < process ref = "myProcessor" />
         <!-- 4: if success then send it to the mock -->
         < to uri = "mock:result" />
     </ route >
</ camelContext >
< bean id = "myProcessor" class = "org.apache.camel.component.jms.tx.JMSTransactionalClientTest$MyProcessor" />

Transaction error handler
When a route is marked as transacted using transacted Camel will automatic use the TransactionErrorHandler as Error Handler. It supports basically the same feature set as the DefaultErrorHandler, so you can for instance use Exception Clause as well.

Using multiple routes with different propagation behaviors

Available as of Camel 2.2
Suppose you want to route a message through two routes and by which the 2nd route should run in its own transaction. How do you do that? You use propagation behaviors for that where you configure it as follows:

  • The first route use PROPAGATION_REQUIRED
  • The second route use PROPAGATION_REQUIRES_NEW

This is configured in the Spring XML file:

< bean id = "PROPAGATION_REQUIRED" class = "org.apache.camel.spring.spi.SpringTransactionPolicy" >
     < property name = "transactionManager" ref = "txManager" />
     < property name = "propagationBehaviorName" value = "PROPAGATION_REQUIRED" />
</ bean >
< bean id = "PROPAGATION_REQUIRES_NEW" class = "org.apache.camel.spring.spi.SpringTransactionPolicy" >
     < property name = "transactionManager" ref = "txManager" />
     < property name = "propagationBehaviorName" value = "PROPAGATION_REQUIRES_NEW" />
</ bean >

Then in the routes you use transacted DSL to indicate which of these two propagations it uses.

from( "direct:mixed" )
     // using required
     .transacted( "PROPAGATION_REQUIRED" )
     // all these steps will be okay
     .setBody(constant( "Tiger in Action" )).beanRef( "bookService" )
     .setBody(constant( "Elephant in Action" )).beanRef( "bookService" )
     // continue on route 2
     .to( "direct:mixed2" );
from( "direct:mixed2" )
     // tell Camel that if this route fails then only rollback this last route
     // by using (rollback only *last*)
     .onException(Exception. class ).markRollbackOnlyLast().end()
     // using a different propagation which is requires new
     .transacted( "PROPAGATION_REQUIRES_NEW" )
     // this step will be okay
     .setBody(constant( "Lion in Action" )).beanRef( "bookService" )
     // this step will fail with donkey
     .setBody(constant( "Donkey in Action" )).beanRef( "bookService" );

Notice how we have configured the onException in the 2nd route to indicate in case of any exceptions we should handle it and just rollback this transaction. 
This is done using the markRollbackOnlyLast which tells Camel to only do it for the current transaction and not globally.

See Also

  • Error handling in Camel
  • TransactionErrorHandler
  • Error Handler
  • JMS

Using This Pattern

If you would like to use this EIP Pattern then please read the Getting Started, you may also find the Architecture useful particularly the description of Endpoint and URIs. Then you could try out some of the Examples first before trying this pattern out.

Transactional Client相关推荐

  1. camel in action

    FIRST STEPS. .......................................................1 1 Meeting Camel 3 1.1 Introduc ...

  2. Apache Camel核心概念

    1. Camel中的相关概念 1.1 ROUTING ENGINE(路由引擎) Camel的路由引擎是消息移动的实际执行者,但并未暴露给开发者:不过作为开发者,我们应该意识到它的存在:并且它做了所有繁 ...

  3. lettuce--Advanced Redis client

    redis官方提供的java client: git地址:https://github.com/mp911de/lettuce Advanced Redis client for thread-saf ...

  4. SAP的client概念

    SAP R/3 Instances and Clients SAP R/3 实例与实集 An SAP R/3 implementation is usually performed with more ...

  5. @transactional注解_为啥同一个类中普通方法调用Spring注解方法,注解会失效?看完你就明白,So easy!...

    Spring注解(如@Transactional.@Cacheable.@Async等),在使用不当时,很可能会失效.失效的情况有很多种,本文我们就来瞅瞅,为啥同一个类中普通方法调用Spring注解方 ...

  6. 单元测试JunitTest加@Transactional事务自动回滚

    问题 在测试事务传播行为的时候,使用单位测试加了@Transactional,一开始是正常,后面出现了异常,即使没有报错的情况下,事务也会自动回滚 代码 @RunWith(SpringRunner.c ...

  7. 【DEBUG】com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Client does not support

    项目场景: 使用 Spring SpringMVC Mybatis 进行整合SSM的过程中,声明一个声明式事务的查询服务,发现无法使用. @Service public class EmpServic ...

  8. @Transactional类内部访问失效原因详解

    一.原理 Spring之所以可以对开启@Transactional的方法进行事务管理,是因为Spring为当前类生成了一个代理类,然后在执行相关方法时,会判断这个方法有没有@Transactional ...

  9. @Transactional注解最容易忽视的三个失效场景!

    @Transactional注解在以下场景中使用,是会失效的,切记! 1.非public方法 spring对注解事务的方法进行校验,修饰符是不是public,不是 public则不会获取@Transa ...

最新文章

  1. python3 ftplib_ftplib — FTP protocol client
  2. DCMTK:解压缩DICOM文件
  3. 前端学习(2197):__WEBPACK_IMPORTED_MODULE_1_vuex__.a.store is not a constructor
  4. Arduino录音时间延长_如何规划好自己的时间让它产生更大价值?
  5. Laravel 启动流程分析 (代码全流程)
  6. springcould结合springbboot微服务 开发@^——^@ 模式讲解
  7. wifi扫描流程图_扫描方法与流程
  8. 简单的Vue计算属性
  9. 记录.NET Core部署到Linux之.NET Core环境搭建(1)
  10. mysql 3列索引_正确理解Mysql的列索引和多列索引
  11. 一招教你如何使用.Net Core 3.0正式版创建Winform程序
  12. Qt系列文章之 mousePressEvent
  13. python魔方方法
  14. 关于SQL的char,varchar字段在导出时切断中文字符显示问号或乱码的问题[原创]
  15. 练手必备,20个Python实战项目含源代码
  16. Excel数据处理函数实践整理
  17. 曾经沧海难为水 除却巫山不是云
  18. 中班机器人教室设计方案_幼儿园创意木工教室解决方案
  19. 部分摄像头接入国标GB28181视频平台无法播放?天视通案例详解
  20. 工作笔记-使用Redis作为Mysql数据库的缓存

热门文章

  1. 移动设备管理软件优劣,南京烽火星空来判别
  2. 地理坐标系,投影坐标系区别
  3. 完整性约束条件:唯一性约束
  4. union与order by 结合使用
  5. 网站管理员请注意jsDelivr因备案被取消CDN访问可能延迟或错误
  6. java边缘检测算子代码_图像边缘检测(Canny 算法)的Java实现
  7. NXP JN5169 使用看门狗定时器
  8. JN516x串口测试
  9. 怀念上世纪90年代的中国摇滚乐
  10. numpy.random.normal函数