`
云上太阳
  • 浏览: 128299 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Spring 多数据库事务的一致性(JTA 和 非JTA)

阅读更多
  1. 这里的项目是基于 Spring4.X + hibernate4.X架构的。最近的一个项目需要两个数据库,一个Oracle,一个是Sqlserver。业务中有一些需求需要跨库事务的一致,举个例子:合同签订保存到基于Oracle的ERP数据库,紧接着下发到Sqlserver的WMS数据库。
  2. 以前听说过JTA分布式事务,google到两种分布式框架:JOTM,atomikos。貌似JOTM简便点,就它了。
  3. 这种方式需要的jar包,首先在JOTM官网下面所有的jar包文件(文末提供下载),下图中选中的jar文件都是需要导入的

     

    首先配置DataSource和SessionFactory

 

<!-- 数据库连接池 -->
	<bean id="dataSource1" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource">
		 <property name="dataSource">  
	        <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">  
	            <property name="transactionManager" ref="jotm" />  
	            <property name="driverName" value="oracle.jdbc.driver.OracleDriver" />  
	            <property name="url" value="jdbc:oracle:thin:@192.168.1.200:1521:orcl" />  
	        </bean>  
	    </property>     
	    <property name="user" value="${jdbc.username}" />  
	    <property name="password" value="${jdbc.password}" /> 
	</bean>
	<bean id="dataSource2" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource"
		>
		 <property name="dataSource">  
	        <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">  
	            <property name="transactionManager" ref="jotm" />  
	            <property name="driverName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />  
	            <property name="url" value="jdbc:sqlserver://192.168.1.200:1433;DatabaseName=middle_db" />  
	        </bean>  
	    </property>     
	    <property name="user" value="${jdbc2.username}" />  
	    <property name="password" value="${jdbc2.password}" />  
	</bean>
	<bean id="sessionFactory1"
		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource1" />
		<property name="packagesToScan">
			<list>
				<value>com.sy.domain</value>
			</list>
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">
					org.hibernate.dialect.OracleDialect
				</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.autoReconnect">true</prop>
				<!-- <prop key="hibernate.hbm2ddl.auto">update</prop> -->
			</props>
		</property>
	</bean>
	
	<bean id="sessionFactory2"
		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource2" />
		<property name="packagesToScan">
			<list>
				<value>com.sy.domain</value>
			</list>
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">
					org.hibernate.dialect.SQLServerDialect
				</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.autoReconnect">true</prop>
				<!-- <prop key="hibernate.hbm2ddl.auto">update</prop> -->
			</props>
		</property>
	</bean>
	<!-- erp.dao -->
	<bean id="commonDao" class="com.sy.dao.impl.CommonDaoImpl" >
		<property name="sessionFactory" ref="sessionFactory1"/>
	</bean>
	<!-- wms.dao -->
	<bean id="commonDao2" class="com.sy.dao.impl.CommonDaoImpl" >
		<property name="sessionFactory" ref="sessionFactory2"/>
	</bean>

  因为我们要操作两个数据库,所以配置两个datasource,两个sessionfactory。需要注意的是我们这里是JOTM和xapool实现的分布式事务。JOTM实现了TransactionManager的功能,xapool通过使用非XA数据库驱动实现了XA数据库驱动的效果,具体这个以后再写文章。这里连接池的配置按照上面的文档配置即可。

 

这里DAO层因为两个数据库所以配置了两个。在Service层可以选择一起注入或者单个注入。

 

下面配置事务

<bean id="jotm" class="com.sy.utils.JotmFactoryBean"/>  
  <bean id="jtaTransactionManager"  class="org.springframework.transaction.jta.JtaTransactionManager">   
     <property name="userTransaction" ref="jotm" />  
	</bean> 
	<tx:advice id="txAdvice" transaction-manager="jtaTransactionManager">
		<tx:attributes>
			<!-- 传播行为 -->
			<tx:method name="save*" propagation="REQUIRED"  rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="insert*" propagation="REQUIRED" rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="add*" propagation="REQUIRED" rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="create*" propagation="REQUIRED" rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="delete*" propagation="REQUIRED" rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="update*" propagation="REQUIRED" rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
			<tx:method name="select*" propagation="SUPPORTS" read-only="true" />
			<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
		</tx:attributes>
	</tx:advice>
	<!-- 切面 -->
	<aop:config expose-proxy="true">
        <aop:pointcut id="bussinessService" expression="execution(* com.sy.service.*.*(..))" />
        <aop:advisor pointcut-ref="bussinessService" advice-ref="txAdvice"/>
    </aop:config>
     <tx:annotation-driven transaction-manager="jtaTransactionManager"/>  

  com.sy.utils.JotmFactoryBean需要手动实现,因为spring4.x不在提供JtaTransactionManager 的默认实现。

 

 

到这里JTA分布式事务的配置已经完成,下面我们看看Service层,和junit测试

@Service
public class CommonServiceImpl implements CommonService{
	
	/** serialVersionUID*/
	private static final long serialVersionUID = -5991777455696969065L;
	@Resource
	private CommonDao commonDao;
	@Resource
	private CommonDao commonDao2;
        //测试两个数据库的事务
	@Override
	public void saveTest() throws MyException {
		commonDao.update("update Dictionary set name=? where id=? ", new Value().add("货主类型").add(1l).getParams());
		commonDao2.update("delete Member where id>25");
                int i = 1/0; //这里异常,前面配置正确的话事务会回滚
	}

}

 

   因为这是一个公共的Service层,需要操作两个数据库,所以两个DAO一起注入。下面是Junit测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring/applicationContext-*.xml"})
public class TestSupport extends AbstractJUnit4SpringContextTests {

        @Autowired
	private CommonService commonService;
	@Test
	public void save() throws MyException{
		commonService.saveTest();
	}
	public CommonService getCommonService() {
		return commonService;
	}
	public void setCommonService(CommonService commonService) {
		this.commonService = commonService;
	}


}

  运行结果如我们所料,事务回滚,OK!

 

下面是直接配置的方式,这种方式是一个老同事那边看到的。当时惊呼不用jta也能实现跨库事务啊。跟普通的单数据库项目配置一样,只是DataSource,SessionFactory,transactionmanager等都是两个。下面是配置

<!-- 数据库连接池 -->
	<bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource"
		destroy-method="close">
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
		<property name="driverClassName" value="${jdbc.driver}" />
		<property name="maxActive" value="${jdbc.maxActive}" />
		<property name="minIdle" value="${jdbc.minIdle}" />
	</bean>
	<bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource"
		destroy-method="close">
		<property name="url" value="${jdbc2.url}" />
		<property name="username" value="${jdbc2.username}" />
		<property name="password" value="${jdbc2.password}" />
		<property name="driverClassName" value="${jdbc2.driver}" />
		<property name="maxActive" value="${jdbc2.maxActive}" />
		<property name="minIdle" value="${jdbc2.minIdle}" />
	</bean>
	<bean id="sessionFactory1"
		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource1" />
		<property name="packagesToScan">
			<list>
				<value>com.sy.domain</value>
			</list>
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">
					org.hibernate.dialect.OracleDialect
				</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.autoReconnect">true</prop>
				<!-- <prop key="hibernate.hbm2ddl.auto">update</prop> -->
			</props>
		</property>
	</bean>
	
	<bean id="sessionFactory2"
		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource2" />
		<property name="packagesToScan">
			<list>
				<value>com.sy.domain</value>
			</list>
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">
					org.hibernate.dialect.SQLServerDialect
				</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.autoReconnect">true</prop>
				<!-- <prop key="hibernate.hbm2ddl.auto">update</prop> -->
			</props>
		</property>
	</bean>
	
	<bean id="commonDao" class="com.sy.dao.impl.CommonDaoImpl" >
		<property name="sessionFactory" ref="sessionFactory1"/>
	</bean>
	
	<bean id="commonDao2" class="com.sy.dao.impl.CommonDaoImpl" >
		<property name="sessionFactory" ref="sessionFactory2"/>
	</bean>
     <bean id="transactionManager1" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory1" />
    </bean>
    
    <bean id="transactionManager2" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory2" />
    </bean>
	
	<tx:advice id="txAdvice1" transaction-manager="transactionManager1">
		<tx:attributes>
			<!-- 传播行为 -->
			<tx:method name="save*" propagation="REQUIRED"  rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="insert*" propagation="REQUIRED" rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="add*" propagation="REQUIRED" rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="create*" propagation="REQUIRED" rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="delete*" propagation="REQUIRED" rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="update*" propagation="REQUIRED" rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
			<tx:method name="select*" propagation="SUPPORTS" read-only="true" />
			<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
		</tx:attributes>
	</tx:advice>
	
	<tx:advice id="txAdvice2" transaction-manager="transactionManager2">
		<tx:attributes>
			<!-- 传播行为 -->
			<tx:method name="save*" propagation="REQUIRED"  rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="insert*" propagation="REQUIRED" rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="add*" propagation="REQUIRED" rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="create*" propagation="REQUIRED" rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="delete*" propagation="REQUIRED" rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="update*" propagation="REQUIRED" rollback-for="com.sy.exceptions.MyException"/>
			<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
			<tx:method name="select*" propagation="SUPPORTS" read-only="true" />
			<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
		</tx:attributes>
	</tx:advice>
	
	<!-- 切面 -->
	<aop:config expose-proxy="true">
        <aop:pointcut id="bussinessService1" expression="execution(* com.sy.service.*.*(..))" />
        <aop:advisor pointcut-ref="bussinessService1" advice-ref="txAdvice1"/>
    </aop:config>
    
    <aop:config expose-proxy="true">
        <aop:pointcut id="bussinessService2" expression="execution(* com.sy.service.*.*(..))" />
        <aop:advisor pointcut-ref="bussinessService2" advice-ref="txAdvice2"/>
    </aop:config>

  这里配置和普通单库配置基本一样,连接池也是自由选择。但是都是两个配置,spring4对多个事务配置也是支持的。测试代码和上面JTA配置的一样,不再给出。

  

 

 

 

  • 大小: 451.9 KB
分享到:
评论
1 楼 hoonick 2018-05-24  
如果一个service的方法中,涉及到5个数据的事务,岂不是要注入五个dao,把相同的代码写五份,有更好的处理方法吗?

相关推荐

    spring cloud事务从入门到精通

    1课程的介绍 2数据库事务的介绍 3mysq|事务讲解 4mysq|-JDBC事务讲解1 5mysqI-JDBC事务讲解2 6搭建一个spring-boot项目 7idea插件easycode 8spring-boot标签事务 ...11spring-boot jta多数据源事务实例

    06丨20%的业务代码的Spring声明式事务,可能都没处理正确

    据我观察,大多数业务开发同学都有事务的概念,也知道如果整体考虑多个数据库操作要么成功要么失败时,需要通过数据库事务来实现多个操作的一致性和原子性。但,在使用上大多仅限于为方法标记 @Transactional,不会...

    分布式事务实践 解决数据一致性

    介绍了Spring的事务机制、事物抽象、内部事务和外部事物,以及常用的几种事务管理的实现,包括DataSource、JPA、JMS、JTA都通过实例进行说明。还有XA以及两阶段提交,并通过实例演示了使用JTA,通过两阶段提交,实现...

    spring_cloud_jta_seata.zip

    微服务使用seata保证事务事务一致性

    spring_boot_jta_atomikos.zip

    同一个服务里面多个数据源事务事务一致性

    spring_boot_jta_bitronix.zip

    同一个服务里面多个数据源事务事务一致性

    spring_boot_jta_seata.zip

    同一个服务里面多个数据源事务事务一致性

    spring最小版所需包

    Spring还提供了唯一的事务管理抽象,它能够在各种底层事务管理技术,例如JTA或者JDBC事务提供一个一致的编程模型。Spring提供了一个用标准Java语言编写的AOP框架,它给POJOs提供了声明式的事务管理和其他企业事务--...

    深入理解Java事务的原理与应用

    ACID是原子性(atomicity)、一致性(consistency)、隔离性 (isolation)和持久性(durability)的缩写。事务的原子性表示事务执行过程中的任何失败都将导致事务所做的任何修改失效。一致性表示 当事务执行失败时...

    Java开发热门项目

    Spring还提供了唯一的事务管理抽象,它能够在各种底层事务管理技术,例如JTA或者JDBC事务提供一个一致的编程模型。Spring提供了一个用标准Java语言编写的AOP框架,它给POJOs提供了声明式的事务管理和其他企业事务--...

    java教程

    Spring还提供了唯一的事务管理抽象,它能够在各种底层事务管理技术,例如JTA或者JDBC事务提供一个一致的编程模型。Spring提供了一个用标准Java语言编写的AOP框架,它给POJOs提供了声明式的事务管理和其他企业事务--...

    Spring Framework 5.3.6

    • 内建的针对JTA和 单个JDBC数据源的一般化策略,使 Spring 的事务支持不要求Java EE环境,这与一般的JTA或者EJB CMT相反。 • JDBC抽象层提供了有针对性的异常等级(不再从SQL异常中提取原始代码),简化了错误...

    java三大框架

    例如,Spring能使用AOP提供声明性事务管理而不通过EJB容器,如果你仅仅需要与单个数据库打交道,甚至不需要一个JTA实现。 Spring为数据存取提供了一个一致的框架,不论是使用的是JDBC还是O/R mapping产品(如...

    经典JAVA.EE企业应用实战.基于WEBLOGIC_JBOSS的JSF_EJB3_JPA整合开发.pdf

    4.2.3 使用JTA全局事务保证多 数据库的一致性 193 4.3 事务隔离、传播属性的设置 198 4.3.1 并发访问和隔离 198 4.3.2 事务属性 199 4.4 EJB的事务管理 201 4.4.1 容器管理事务(CMT) 201 4.4.2 Bean管理事务(BMT) ...

    数据管理平台 Apache Geode.zip

    默认情况下, Geode 客户端和服务器使用端口 40404 和多播来互相发现.Geode 包含了如下的特性:结合冗余, 复制, 和 "shared nothing" 的一致性架构来交付 '自动防故障' 的可靠性和高性能.水平扩展到数千个缓存成员, ...

    ibatis 开发指南(pdf)

    相对Hibernate 等 “全自动”ORM 机制而言,ibatis 以SQL 开发的工作量和数据库移植性上的让步,为系统 设计提供了更大的自由空间。作为“全自动”ORM 实现的一种有益补充,ibatis 的出现显 得别具意义。...

Global site tag (gtag.js) - Google Analytics