Spring 多数据源 -------集成JTA-Atomikos实现动态切换数据源

背景:

项目中业务分库需要使用到动态数据源,本来使用的Spring的AbstractRoutingDataSourced来实现,但是测试时发现其无法控制跨库的事务,就改为使用JTA-Atomikos来做丰台数据源了。JTA集成动态数据源比较简单,快捷。并不需要修改多少代码,只需更改下我们的配置文件就ok了。

JTA的定义:引用自百度)

Java事务API(Java Transaction API,简称JTA ) 是一个Java企业版 的应用程序接口,在Java环境中,允许完成跨越多个XA资源的分布式事务. 
一个分布式的事务涉及一个事务管理器和一个或者多个资源管理器。一个资源管理器是任何类型的持久性的数据存储。事务管理器负责协调所有事务参与者之间的通信。

常见的JTA实现有以下几种: 
1.J2EE容器所提供的JTA实现(JBoss中jndi容器事物配置) 
2.独立的JTA实现:如JOTM,Atomikos等开源事物管理器 
这些实现可以应用在那些不使用J2EE应用服务器的环境里用以提供分布事事务保证。如Tomcat,Jetty以及普通的java应用。

本篇中我们使用的是Atomikos。

1.Atomikos的maven依赖配置如下:

  <!-- atomikos 依赖-->
       <dependency>
			<groupId>com.atomikos</groupId>
			<artifactId>transactions-jdbc</artifactId>
		</dependency>
		<dependency>  
            <groupId>com.atomikos</groupId>  
            <artifactId>atomikos-util</artifactId>  
        </dependency>  
        <dependency>  
            <groupId>com.atomikos</groupId>  
            <artifactId>transactions</artifactId>  
        </dependency>  
         <dependency>  
            <groupId>com.atomikos</groupId>  
            <artifactId>transactions-jta</artifactId>  
        </dependency>  
        <dependency>  
            <groupId>com.atomikos</groupId>  
            <artifactId>transactions-api</artifactId>  
        </dependency> 

 <!-- jta 事务 -->
		<dependency>
			<groupId>javax.transaction</groupId>
			<artifactId>jta</artifactId>
		</dependency>

 

2.为每个数据库配置数据源,这里注意mysql和Oracle数据库的xaDataSourceClassName 配置是不一样的

oracle数据库:oracle.jdbc.xa.client.OracleXADataSource;
myslq数据库:com.mysql.jdbc.jdbc2.optional.MysqlXADataSource.
   	 <aop:aspectj-autoproxy />
   	  <!-- 读取数据库配置文件 -->
   	 <context:property-placeholder location="classpath:jdbc.properties" ignore-unresolvable="true"/>
 

	 <!-- 两个数据源的功用配置,方便下面直接引用 -->
     <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" 
             destroy-method="close" abstract="true"> 
        <property name="poolSize" value="10" /> 
        <property name="minPoolSize" value="10"/> 
        <property name="maxPoolSize" value="30"/> 
        <property name="borrowConnectionTimeout" value="60"/>  <!--获取连接失败重新获等待最大时间,在这个时间内如果有可用连接,将返回-->
        <property name="reapTimeout" value="20"/> <!--最大获取数据时间,如果不设置这个值,Atomikos使用默认的5分钟,那么在处理大批量数据读取的时候,一旦超过5分钟,就会抛出类似 Resultset is close 的错误.-->        
        <property name="maxIdleTime" value="60"/>    <!--最大闲置时间,超过最小连接池连接的连接将将关闭-->
        <property name="maintenanceInterval" value="60" />  <!--连接回收时间-->    
        <property name="loginTimeout" value="60" />     <!--java数据库连接池,最大可等待获取datasouce的时间-->
        <property name="logWriter" value="60"/>
        <property name="testQuery">
            <value>select 1</value>
        </property>
    </bean> 
	
	  <!-- 配置第一个数据源 -->
    <bean id="busDataSource" parent="abstractXADataSource">
         <!-- value只要两个数据源不同就行,随便取名 -->
        <property name="uniqueResourceName" value="busDataSource" />
        <property name="xaDataSourceClassName"
            value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
                <prop key="URL">${jdbc.busUrl}</prop>
                <prop key="user">${jdbc.username}</prop>
                <prop key="password">${jdbc.password}</prop>
            </props>
        </property>
    </bean>

    <!-- 配置第二个数据源-->
    <bean id="dspDataSource" parent="abstractXADataSource">
        <!-- value只要两个数据源不同就行,随便取名 -->
        <property name="uniqueResourceName" value="dspDataSource" />
        <property name="xaDataSourceClassName"
            value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
                <prop key="URL">${jdbc.dspUrl}</prop>
                <prop key="user">${jdbc.username}</prop>
                <prop key="password">${jdbc.password}</prop>
            </props>
        </property>
    </bean>

 

 

3.再为每一个数据源对应的dao层,也就是mapper层,创建其对应的SqlSessionFactory,并建立其与dao层包名的对应关系。

    <bean id="busSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
         <property name="dataSource" ref="busDataSource" /><!--  bus dataSource-->
         <property name="mapperLocations" value="classpath*:cn/hccake/test/bus/model/mappers/**/*.xml" />
     </bean>

     <bean id="dspSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
         <property name="dataSource" ref="dspDataSource" /><!-- dsp dataSource-->
         <property name="mapperLocations" value="classpath*:cn/hccake/test/dsp/model/mappers/**/*.xml" />
     </bean>
	


    
    <!--scan for mappers and let them be autowired-->
     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
         <property name="basePackage" value="cn.moppo.domino.bus.dao"/>
         <property name="sqlSessionFactoryBeanName" value="busSqlSessionFactory" />
     </bean>

     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
         <property name="basePackage" value="cn.moppo.domino.dsp.dao"/>
         <property name="sqlSessionFactoryBeanName" value="dspSqlSessionFactory" />
    </bean>
    

4.最后  配置对应的事物管理器:atomikosTransactionManager;
atomikosUserTransaction将其注入到spring管理的JTA事物管理器中 org.springframework.transaction.jta.JtaTransactionManager

    <!-- 开始事物注解扫描 -->
     <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

 
<!-- 事务管理 -->
 	<bean id="atomikosUserTransaction"
        class="com.atomikos.icatch.jta.UserTransactionImp">
        <description>UserTransactionImp</description>
        <property name="transactionTimeout" value="300"/>
    </bean>

    <bean id="atomikosTransactionManager"
        class="com.atomikos.icatch.jta.UserTransactionManager"
        init-method="init" destroy-method="close">
        <description>UserTransactionManager</description>
        <property name="forceShutdown">
            <value>true</value>
        </property>
    </bean>

    <bean id="transactionManager"
        class="org.springframework.transaction.jta.JtaTransactionManager">
        <description>JtaTransactionManager</description>
        <property name="transactionManager">
            <ref bean="atomikosTransactionManager" />
        </property>
        <property name="userTransaction">
            <ref bean="atomikosUserTransaction" />
        </property>
        <property name="allowCustomIsolationLevels" value="true"/> <!-- 必须设置,否则程序出现异常 JtaTransactionManager does not support custom isolation levels by default -->
    </bean> 


    
    <!-- 简单的事务切面控制 -->
      <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="select" read-only="true" />
            <tx:method name="find*" read-only="true" />
  
            <tx:method name="insert*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
             
            <tx:method name="*" propagation="SUPPORTS" rollback-for="Throwable" />
        </tx:attributes>
    </tx:advice>
    
     <aop:config>
        <aop:pointcut id="interceptorPointCuts"
            expression="execution(* cn.hccake.test.*..*Service.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts" />
    </aop:config>

5.使用时,在简单的切面控制事务之外,也可在类名或者方法名上使用@Transactional注解来进行事务控制,和spring事务管理的控制方法是一模一样的

@Service
@Transactional
public class TestService {

	@Autowired
	private AdTypeService adTypeService;
	@Autowired
	private BusTypeService busTypeService;
	@Autowired
	private AdTypeDao adTypeDao;
	
	public void updateTest(){
		adTypeService.updateTest("test1");
		busTypeService.updateTest("test2");
		
		adTypeDao.updateTest("aaa");
		throw new RuntimeException("分布式回滚控制");
		
	}
	
}

 

转载于:https://my.oschina.net/hccake/blog/1591304

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值