使用Atomikos来进行分布式事务(XA)开发
结合Spring使用
和Spring集成的时候,不需要JNDI服务器注意,不支持Spring的事务传播性:PROPAGATION_NESTED
JTA事务管理器配置样例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<bean id="localLogAdministrator" class="com.atomikos.icatch.admin.imp.LocalLogAdministrator" /> <bean id="userTransactionService" class="com.atomikos.icatch.config.UserTransactionServiceImp" init-method="init" destroy-method="shutdownForce"> <constructor-arg> <!-- 配置Atomikos属性 --> <props> <prop key="com.atomikos.icatch.service">com.atomikos.icatch.standalone.UserTransactionServiceFactory</prop> </props> </constructor-arg> <property name="initialLogAdministrators"> <list> <ref bean="localLogAdministrator" /> </list> </property> </bean> <!-- Atomikos 事务管理器配置 --> <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close" depends-on="userTransactionService"> <property name="startupTransactionService" value="false" /> <!-- close()时是否强制终止事务 --> <property name="forceShutdown" value="false" /> </bean> <!-- Atomikos UserTransaction配置 --> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp" depends-on="userTransactionService"> <property name="transactionTimeout" value="300" /> </bean> <!-- JTA事务管理器 --> <bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" depends-on="userTransactionService"> <property name="transactionManager" ref="atomikosTransactionManager" /> <property name="userTransaction" ref="atomikosUserTransaction" /> </bean> |
配合消息中间件使用(ActiveMQ)的配置样例:
3.9以前的版本,必须设置sessionTransacted为true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<bean id="xaFactory" class="org.apache.activemq.ActiveMQXAConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61616" /> </bean> <bean id="jmsFactory" class="com.atomikos.jms.AtomikosConnectionFactoryBean" init-method="init" destroy-method="close"> <property name="uniqueResourceName" value="amq" /> <property name="xaConnectionFactory" ref="xaFactory" /> </bean> <bean id="tipsMessageListener" class="cc.gmem.demo.TipsMessageListener" /> <!-- 接收消息 --> <jms:listener-container container-type="default" connection-factory="jmsFactory" transaction-manager="jtaTransactionManager" session-transacted="true"> <jms:listener destination="TIPS.10000.BATCH" ref="tipsMessageListener" method="onMessage" /> </jms:listener-container> <!-- 发送消息 --> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory"> <ref bean="jmsFactory" /> </property> <property name="receiveTimeout" value="1000" /> <property name="sessionTransacted" value="true" /> </bean> |
配合数据库使用的配置样例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <!-- 必须为Hibernate指定一个Atomikos JTA/XA 数据源 --> <property name="dataSource" ref="dataSource" /> </bean> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="dataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close"> <property name="uniqueResourceName" value="mysqlDataSource" /> <property name="xaDataSourceClassName"> <value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value> </property> <property name="xaProperties"> <props> <prop key="databaseName">gmem</prop> <prop key="serverName">localhost</prop> <prop key="port">3306</prop> <prop key="user">user</prop> <prop key="password">pswd</prop> <prop key="url">jdbc:mysql://localhost:3306/gmem</prop> </props> </property> <property name="minPoolSize" value="10"> </bean> <bean id="dataSourceOracle" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close"> <property name="uniqueResourceName" value="oralceDataSource" /> <property name="xaDataSourceClassName"> <value>oracle.jdbc.xa.client.OracleXADataSource</value> </property> <property name="xaProperties"> <props> <prop key="user">gmemx</prop> <prop key="password">pswd</prop> <prop key="URL">jdbc:oracle:thin:@192.168.0.25:1521:DEV</prop> </props> </property> </bean> |
不使用Spring,单独和Hibernate集成
Hibernate配置示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="connection.datasource">ds0</property> <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property> <property name="current_session_context_class">jta</property> <property name="transaction.manager_lookup_class">com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup</property> <property name="transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</property> <property name="connection.release_mode">auto</property> <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property> <property name="show_sql">true</property> <mapping resource="cc/gmem/demo/vo/User.hbm.xml"/> </session-factory> </hibernate-configuration> |
jndi.properties配置示例:
1 |
java.naming.factory.initial=org.apache.naming.java.javaURLContextFactory |
jta.properties配置示例:
1 2 3 |
com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory com.atomikos.icatch.automatic_resource_registration=true com.atomikos.icatch.console_log_level=DEBUG |
通过编程方式初始化数据源,并绑定到JNDI,进行编程式事务控制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.transaction.UserTransaction; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import com.atomikos.jdbc.SimpleDataSourceBean; Context ctx = new InitialContext(); SimpleDataSourceBean ds0 = new SimpleDataSourceBean(); ds0.setUniqueResourceName("ds0"); ds0.setXaDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"); ds0.setXaDataSourceProperties("databaseName=gmem"); ds0.setConnectionPoolSize(10); ctx.rebind("ds0", ds0); SimpleDataSourceBean ds1 = = new SimpleDataSourceBean(); ds0.setUniqueResourceName("ds1"); ds0.setXaDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"); ds0.setXaDataSourceProperties("databaseName=gmembkp"); ds0.setConnectionPoolSize(10); ctx.rebind("ds1", ds1); ctx.close(); UserTransaction tx = new com.atomikos.icatch.jta.UserTransactionImp(); SessionFactory sf0 = new Configuration().configure("/hibernate0.cfg.xml").buildSessionFactory(); SessionFactory sf1 = new Configuration().configure("/hibernate1.cfg.xml").buildSessionFactory(); tx.setTransactionTimeout(60); tx.begin(); try { //执行DML操作 userTransaction.commit(); } catch (Exception ex) { tx.rollback(); } sf0.close(); sf1.close(); ds0.close(); ds1.close(); |
一些常见问题
com.atomikos.icatch.RollbackException: Prepare: NO vote
详细参考:http://www.atomikos.com/Documentation/JtaProperties
注意事务的超时设置:UserTransaction.setTransactionTimeout()
另外,超时设置无法超过最大值限制:jta.properties中的com.atomikos.icatch.max_timeout
Oracle 9.2.0.8.0版本下操作blob导致挂起(Halt)
10.x驱动无该问题,可能Oracle驱动的bug。INSERT时没有问题,UPDATE时,如果1个blob也没问题,2个就挂起。
可以禁用Hibernate批量操作来临时解决:jdbc.batch_size=0
Leave a Reply