由于项目中用到了操作多个数据库,并且要在通过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); } }