最近因为业务需要,因为客票系统的数据量非常之大,需要对数据进行分散部署,而且数据库分布在不同城市,对于单数据库的操作来说事物很简单就可以处理好,但是对于多数据库来说,如何保持数据的一致性,则需要分布式事物处理。
所谓分布式事务,就是数据源不止一个,可能有两个或更多,在一些大型应用中常常出现,常用的解决办法就是使用JTA,但是如果使用tomcat(用其他容器也一样可以使用此方式操作,当然直接使用容器支持的分布式事物是最好的,如jboss或其他的web容器)作为容器则它本身不支持JTA事物,所以需要用到第三方库,此例中使用Atomikosessential库(具体信息可以通过搜索得到,多动手吧!)。
本例环境(本例只模拟两个数据库的情况,多个数据库就多写几个数据源即可,在此不多复述了)
数据库:数据库1(广州mysql,地址是10.108.1.14);数据库2(上海oracle,地址是10.10.128.126),两个数据库分别部署在网络上的不同地址;
测试内容:同时往两个数据库添加一条数据,分别测试合法数据和不合法数据的情况,如果合法应该同时提交事务,不合法的则会回滚事务
使用的技术内容:大家常用的ssh(当然也可以不用这些,其他的技术内容完全可以,只是为了照顾大家的一个使用习惯)
演示内容
首先配置ssh
hibernate的配置放到spring配置文件中,配置如下:
spring-ds.xml文件内容
[CODE]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="webDs" class="com.atomikos.jdbc.AtomikosDataSourceBean"
init-method="init" destroy-method="close">
<property name="uniqueResourceName">
<value>mysql/main</value>
</property>
<property name="xaDataSourceClassName">
<value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value>
</property>
<property name="xaProperties">
<props>
<prop key="user">root</prop>
<prop key="password"></prop>
<prop key="URL">jdbc:mysql://127.0.0.1:3306/localmember?zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8
</prop>
</props>
</property>
<property name="poolSize">
<value>50</value>
</property>
</bean>
<bean id="storeDs" class="com.atomikos.jdbc.AtomikosDataSourceBean"
init-method="init" destroy-method="close">
<property name="uniqueResourceName">
<value>oracle/main</value>
</property>
<property name="xaDataSourceClassName">
<value>oracle.jdbc.xa.client.OracleXADataSource</value>
</property>
<property name="xaProperties">
<props>
<prop key="user">dbusrcard</prop>
<prop key="password">futurecard</prop>
<prop key="URL">jdbc:oracle:thin:@localhost:1521:xe</prop>
</props>
</property>
<property name="poolSize">
<value>30</value>
</property>
</bean>
</beans>
[/CODE]
spring-dao.xml文件内容
[CODE]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<bean id="storeSessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="storeDs" />
<property name="mappingResources">
<list>
<value>net/hlin/multitx/entity/StoreTestTb.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
hibernate.show_sql=true
hibernate.format_sql=true
</value>
</property>
</bean>
<bean id="webSessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="webDs" />
<property name="mappingResources">
<list>
<value>net/hlin/multitx/entity/WebTestTb.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.show_sql=true
hibernate.format_sql=true
</value>
</property>
</bean>
<bean id="webDao" class="net.hlin.multitx.dao.WebDao">
<property name="sessionFactory" ref="webSessionFactory" />
</bean>
<bean id="storeDao" class="net.hlin.multitx.dao.StoreDao">
<property name="sessionFactory" ref="storeSessionFactory" />
</bean>
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp" />
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
init-method="init" destroy-method="close">
<property name="forceShutdown">
<value>true</value>
</property>
</bean>
<bean id="txManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager">
<ref bean="atomikosTransactionManager" />
</property>
<property name="userTransaction">
<ref bean="atomikosUserTransaction" />
</property>
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor advice-ref="txAdvice"
pointcut="execution(* net.hlin.multitx.service.*.*(..))" />
</aop:config>
<!--因为业务代码配置只有2行,所以直接写在这里了,正式开发环境中请遵守开发规范-->
<context:annotation-config />
<context:component-scan base-package="net.hlin.multitx.service" />
</beans>
[/CODE]
hbm文件省略,按各自爱好写即可
业务逻辑类TestService.java(名字不怎么规范,自己换吧!)
[CODE]
package net.hlin.multitx.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import net.hlin.multitx.dao.StoreDao;
import net.hlin.multitx.dao.WebDao;
import net.hlin.multitx.entity.TestTb;
@Component
public class TestService {
@Autowired
private WebDao webDao;
@Autowired
private StoreDao storeDao;
public void testMethod() {
//测试成功的
webDao.add(new TestTb(100, "a"));
storeDao.add(new TestTb(102, "b"));
/*第一次测试完成后接着把这段解注释,再次运行业务类这次因为有一次数据重复,数据要回滚
webDao.add(new TestTb(101, "a"));
storeDao.add(new TestTb(102, "b"));
*/
}
public void setWebDao(WebDao webDao) {
this.webDao = webDao;
}
public void setStoreDao(StoreDao storeDao) {
this.storeDao = storeDao;
}
}
[/CODE]
最终的测试类 TestServiceTest .java
[CODE]
package net.hlin.multitx.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring-ds.xml", "classpath:spring-dao.xml"})
public class TestServiceTest {
@Autowired
private TestService testService;
@Test
public void testTestMethod() {
testService.testMethod();
}
}