Atomikos实现分布式事务开发小结
程序员文章站
2022-05-23 12:27:41
...
由于项目中用到了操作多个数据库,并且要在通过Service方法里面完成,为了保证事务同步,引用了Atomikos,可参考http://www.atomikos.com/Documentation/。
1、加入Atomikos的相关类库,Maven项目中配置如下:
<dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jdbc</artifactId> <version>3.7.0</version> </dependency>
2、配置数据源,使用atomikos自带的datasource实现类,如AtomikosNonXADataSourceBean,AtomikosDataSourceBean等,经测试AtomikosDataSourceBean这类对数据库的要求挺多的,特别是Oracle,于是采用AtomikosNonXADataSourceBean。我配置了一个MySQL数据源,一个Oracle数据源如下:
<bean id="mysqlDS" class="com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean" init-method="init"
destroy-method="close" p:uniqueResourceName="mysql_ds" p:testQuery="select 1 ">
<property name="driverClassName" value="${mysql.connection.driverClass}" />
<property name="url" value="${mysql.connection.url}" />
<property name="user" value="${mysql.connection.username}" />
<property name="password" value="${mysql.connection.password}" />
<property name="poolSize" value="5" />
<property name="maxPoolSize" value="30" />
</bean>
<bean id="oracleDS" class="com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean" init-method="init"
destroy-method="close" p:uniqueResourceName="oracle_ds" p:testQuery="select 1 from dual ">
<property name="driverClassName" value="${oracle.connection.driverClass}" />
<property name="url" value="${oracle.connection.url}" />
<property name="user" value="${oracle.connection.username}" />
<property name="password" value="${oracle.connection.password}" />
<property name="poolSize" value="5" />
<property name="maxPoolSize" value="30" />
</bean>
3、配置SessionFacotry ,不同的数据源对应不同的SessionFacotry.
<bean id="mySqlSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="mysqlDS" /> <property name="packagesToScan"> <list> <value>com.lyl.**.entity</value> </list> </property> <property name="entityInterceptor"> <bean class="com.lyl.base.BaseDaoInterceptor"></bean> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">${mysql.dialect}</prop> <prop key="hibernate.hbm2ddl.auto">${mysql.hbm2ddl.auto}</prop> <prop key="hibernate.jdbc.fetch_size">50</prop> <prop key="hibernate.jdbc.batch_size">20</prop> <prop key="hibernate.query.factory_class">org.hibernate.hql.ast.ASTQueryTranslatorFactory </prop> <prop key="hibernate.jdbc.use_scrollable_resultset">false</prop> <prop key="hibernate.use_outer_join">true</prop> <prop key="hibernate.show_sql">false</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.cache.use_query_cache">true</prop> <prop key="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider </prop> </props> </property> </bean> <bean id="oracleSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="oracleDS" /> <property name="packagesToScan"> <list> <value>com.lyl.**.entity</value> </list> </property> <property name="entityInterceptor"> <bean class="com.lyl.base.BaseDaoInterceptor"></bean> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">${oracle.dialect}</prop> <prop key="hibernate.hbm2ddl.auto">${oracle.hbm2ddl.auto}</prop> <prop key="hibernate.jdbc.fetch_size">50</prop> <prop key="hibernate.jdbc.batch_size">20</prop> <prop key="hibernate.query.factory_class">org.hibernate.hql.ast.ASTQueryTranslatorFactory </prop> <prop key="hibernate.jdbc.use_scrollable_resultset">false</prop> <prop key="hibernate.use_outer_join">true</prop> <prop key="hibernate.show_sql">false</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.cache.use_query_cache">true</prop> <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop> <prop key="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider </prop> </props> </property> </bean>
4、配置事务管理。
根据需要配置了两个数据库事务,一个管理单个数据库操作,另一个管理多个数据库操作,配置不同的切面,针对不同的类,采取不同的事务方式。
<bean id="baseTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="mySqlSessionFactory" /> </bean> <tx:advice id="baseTxAdvice" transaction-manager="baseTransactionManager"> <tx:attributes> <tx:method name="get*" read-only="true" /> <tx:method name="query*" read-only="true" /> <tx:method name="find*" read-only="true" /> <tx:method name="load*" read-only="true" /> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <aop:config proxy-target-class="true"> <aop:advisor pointcut="execution(* com.lyl..*Service.*(..))" advice-ref="baseTxAdvice" /> </aop:config>
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close"> <property name="forceShutdown" value="true" /> </bean> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"> <property name="transactionTimeout" value="300" /> </bean> <!-- JTA事务管理器 --> <bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManager" ref="atomikosTransactionManager" /> <property name="userTransaction" ref="atomikosUserTransaction" /> </bean> <tx:advice id="jtaTxAdvice" transaction-manager="jtaTransactionManager"> <tx:attributes> <tx:method name="get*" read-only="true" /> <tx:method name="query*" read-only="true" /> <tx:method name="find*" read-only="true" /> <tx:method name="load*" read-only="true" /> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <aop:config proxy-target-class="true"> <aop:advisor pointcut="execution(* com.lyl..*Biz.*(..))" advice-ref="jtaTxAdvice" /> </aop:config>
5、配置两个操作不同数据库DAO类。
<bean id="mySqlHibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"> <constructor-arg name="sessionFactory"> <ref bean="mySqlSessionFactory" /> </constructor-arg> </bean> <bean id="oracleHibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"> <constructor-arg name="sessionFactory"> <ref bean="oracleSessionFactory" /> </constructor-arg> </bean> <bean id="mySqlBaseDao" class="com.lyl.base.BaseDao"> <property name="hibernateTemplate" ref="mySqlHibernateTemplate"></property> </bean> <bean id="oracleBaseDao" class="com.lyl.base.BaseDao"> <property name="hibernateTemplate" ref="oracleHibernateTemplate"></property> </bean>
6、需要执行分布式事务的Service类。
@Service
public class AccountBiz {
@Resource
private BaseDao mySqlBaseDao;
@Resource
private BaseDao oracleBaseDao;
public void transer(Double amount) {
Account_A a=new Account_A();
a.setAid(1L);
Account_A aa=(Account_A)mySqlBaseDao.findById(a);
Account_B b=new Account_B();
b.setBid(1L);
Account_B bb=(Account_B)oracleBaseDao.findById(b);
//bb.setIdNo("88888888");
Date date=new Date();
aa.setOpDate(date);
aa.setAmount(aa.getAmount()+amount);
bb.setOpDate(date);
bb.setAmount(bb.getAmount()-amount);
mySqlBaseDao.update(aa);
oracleBaseDao.update(bb);
}
}