Spring分布式事务实现

本文介绍了Spring如何实现分布式事务,特别是当与WebSphere或Tomcat等应用服务器配合时。在WebSphere中,Spring的JtaTransactionManager可以利用其内置的JTA支持。而在Tomcat这样的非JTA环境中,可以通过JOTM或Atomikos这样的第三方事务管理器来实现。文章提供了一个使用JOTM的简单示例,包括Dao接口和实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分布式事务是指操作多个数据库之间的事务,spring的org.springframework.transaction.jta.JtaTransactionManager,提供了分布式事务支持。如果使用WAS的JTA支持,把它的属性改为WebSphere对应的TransactionManager。

在tomcat下,是没有分布式事务的,不过可以借助于第三方软件jotm(Java Open Transaction Manager )和AtomikosTransactionsEssentials实现,在spring中分布式事务是通过jta(jotm,atomikos)来进行实现。

一、使用JOTM例子

(1) Dao及实现

GenericDao接口:

public interface GenericDao {
 public int save(String ds, String sql, Object[] obj) throws Exception; 
 public int findRowCount(String ds, String sql); 
}

GenericDaoImpl 实现:

public class GenericDaoImpl implements GenericDao{
private JdbcTemplate jdbcTemplateA;
private JdbcTemplate jdbcTemplateB;
public void setJdbcTemplateA(JdbcTemplate jdbcTemplate) {
 this.jdbcTemplateA = jdbcTemplate;
}
public void setJdbcTemplateB(JdbcTemplate jdbcTemplate) {
 this.jdbcTemplateB = jdbcTemplate;
}
public int save(String ds, String sql, Object[] obj) throws Exception{
 if(null == ds || "".equals(ds)) return -1;
 try{
 if(ds.equals("A")){
 return this.jdbcTemplateA.update(sql, obj);
 }else{
 return this.jdbcTemplateB.update(sql, obj);
 }
 }catch(Exception e){
 e.printStackTrace();
 throw new Exception("执行" + ds + "数据库时失败!");
 }
}
public int findRowCount(String ds, String sql) {
 if(null == ds || "".equals(ds)) return -1;
 if(ds.equals("A")){
 return this.jdbcTemplateA.queryForInt(sql);
 }else{
 return this.jdbcTemplateB.queryForInt(sql);
 }
}
 
}</pre>
(2) Service及实现 
UserService 接口:
public interface UserService {
 public void saveUser() throws Exception;
}
 
 
UserServiceImpl 实现:
public class UserServiceImpl implements UserService{
private GenericDao genericDao;
public void setGenericDao(GenericDao genericDao) {
 this.genericDao = genericDao;
}
public void saveUser() throws Exception {
 String userName = "user_" + Math.round(Math.random()*10000);
 System.out.println(userName);
 StringBuilder sql = new StringBuilder();
 sql.append(" insert into t_user(username, gender) values(?,?); ");
 Object[] objs = new Object[]{userName,"1"};
 genericDao.save("A", sql.toString(), objs);
 sql.delete(0, sql.length());
 sql.append(" insert into t_user(name, sex) values(?,?); ");
 objs = new Object[]{userName,"男的"};//值超出范围
 genericDao.save("B", sql.toString(), objs);
}
 
}</pre> 
 
(3) applicationContext-jotm.xml 
<?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:context="http://www.springframework.org/schema/context" 
 xmlns:aop="http://www.springframework.org/schema/aop" 
 xmlns:tx="http://www.springframework.org/schema/tx" 
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 
 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd 
 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd 
 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<description>springJTA</description>
<!--指定Spring配置中用到的属性文件--> 
<bean id="propertyConfig" 
 class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
 <property name="locations"> 
 <list> 
 <value>classpath:jdbc.properties</value> 
 </list> 
 </property> 
</bean> 
<!-- JOTM实例 -->
<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean">
 <property name="defaultTimeout" value="500000"/>
</bean>
<!-- JTA事务管理器 -->
<bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> 
 <property name="userTransaction" ref="jotm" /> 
</bean>
<!-- 数据源A --> 
<bean id="dataSourceA" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown"> 
 <property name="dataSource"> 
 <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown"> 
 <property name="transactionManager" ref="jotm"/> 
 <property name="driverName" value="${jdbc.driver}"/> 
 <property name="url" value="${jdbc.url}"/> 
 </bean> 
 </property> 
 <property name="user" value="${jdbc.username}"/> 
 <property name="password" value="${jdbc.password}"/> 
</bean> 
<!-- 数据源B --> 
<bean id="dataSourceB" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown"> 
 <property name="dataSource"> 
 <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown"> 
 <property name="transactionManager" ref="jotm"/> 
 <property name="driverName" value="${jdbc2.driver}"/> 
 <property name="url" value="${jdbc2.url}"/> 
 </bean> 
 </property> 
 <property name="user" value="${jdbc2.username}"/> 
 <property name="password" value="${jdbc2.password}"/> 
</bean> 
<bean id = "jdbcTemplateA" 
 class = "org.springframework.jdbc.core.JdbcTemplate"> 
 <property name = "dataSource" ref="dataSourceA"/> 
</bean>
<bean id = "jdbcTemplateB" 
 class = "org.springframework.jdbc.core.JdbcTemplate"> 
 <property name = "dataSource" ref="dataSourceB"/> 
</bean> 
<!-- 事务切面配置 --> 
<aop:config> 
 <aop:pointcut id="pointCut"
 expression="execution(* com.logcd.service..*.*(..))"/><!-- 包及其子包下的所有方法 -->
 <aop:advisor pointcut-ref="pointCut" advice-ref="txAdvice"/> 
 <aop:advisor pointcut="execution(* *..common.service..*.*(..))" advice-ref="txAdvice"/>
</aop:config> 
<!-- 通知配置 --> 
<tx:advice id="txAdvice" transaction-manager="jtaTransactionManager"> 
 <tx:attributes> 
 <tx:method name="delete*" rollback-for="Exception"/> 
 <tx:method name="save*" rollback-for="Exception"/> 
 <tx:method name="update*" rollback-for="Exception"/> 
 <tx:method name="find*" read-only="true" rollback-for="Exception"/> 
 </tx:attributes> 
</tx:advice> 
<bean id="genericDao" class="com.logcd.dao.impl.GenericDaoImpl" autowire="byName"> </bean>
<bean id="userService" class="com.logcd.service.impl.UserServiceImpl" autowire="byName"> </bean>
 
</beans></pre> 
(4) 测试 
public class TestUserService{
private static UserService userService;
@BeforeClass
public static void init(){
 ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext-jotm.xml");
 userService = (UserService)app.getBean("userService");
}
@Test
public void save(){
 System.out.println("begin...");
 try{
 userService.saveUser();
 }catch(Exception e){
 System.out.println(e.getMessage());
 }
 System.out.println("finish...");
}
 
}</pre>
二、关于使用atomikos实现 
(1) 数据源配置 
<bean id="dataSourceA" class="com.atomikos.jdbc.SimpleDataSourceBean" init-method="init" destroy-method="close">
 <property name="uniqueResourceName">
 <value>${datasource.uniqueResourceName}</value>
 </property>
 <property name="xaDataSourceClassName"> 
 <value>${database.driver_class}</value> 
 </property> 
 <property name="xaDataSourceProperties">
 <value>URL=${database.url};user=${database.username};password=${database.password}</value> 
 </property> 
 <property name="exclusiveConnectionMode"> 
 <value>${connection.exclusive.mode}</value> 
 </property>
 <property name="connectionPoolSize"> 
 <value>${connection.pool.size}</value>
 </property>
 <property name="connectionTimeout">
 <value>${connection.timeout}</value>
 </property>
 <property name="validatingQuery"> 
 <value>SELECT 1</value> 
 </property> 
</bean>
 
(2)、事务配置 
 <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="${transaction.timeout}"/> 
</bean>
<!-- JTA事务管理器 --> 
<bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> 
 <property name="transactionManager" ref="atomikosTransactionManager"/> 
 <property name="userTransaction" ref="atomikosUserTransaction"/> 
</bean>
<!-- 事务切面配置 --> 
<aop:config> 
 <aop:pointcut id="serviceOperation" expression="execution(* *..service*..*(..))"/> 
 <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/> 
</aop:config>
<!-- 通知配置 -->
<tx:advice id="txAdvice" transaction-manager="springTransactionManager"> 
 <tx:attributes>
 <tx:method name="*" rollback-for="Exception"/> 
 </tx:attributes> 
</tx:advice> </pre><br />
 
有关JTA
JTA全称为Java Transaction API,顾名思义JTA定义了一组统一的事务编程的接口,这些接口如下:
 
XAResource 
XAResource接口是对实现了X/Open CAE规范的资源管理器 (Resource Manager,数据库就是典型的资源管理器) 的抽象,它由资源适配器 (Resource Apdater) 提供实现。XAResource是支持事务控制的核心。
Transaction
Transaction接口是一个事务实例的抽象,通过它可以控制事务内多个资源的提交或者回滚。二阶段提交过程也是由Transaction接口的实现者来完成的。
TransactionManager
托管模式 (managed mode) 下,TransactionManager接口是被应用服务器调用,以控制事务的边界的。
 
UserTransaction
非托管模式 (non-managed mode) 下,应用程序可以通过UserTransaction接口控制事务的边界
 
托管模式下的事务提交场景

Spring分布式事务实现

 

注意:在上图中3和5的步骤之间省略了应用程序对资源的操作 (如CRUD)。另外,应用服务器什么时机 enlistResource,又是什么时候delistResource呢?这在后文中会解释。
 
有关JCA
下图为JCA的架构图

Spring分布式事务实现

 

中间涉及元素说明如下:
1)Enterprise Information System
简称EIS,在JTA中它又被称为资源管理器。典型的EIS有数据库,事务处理系统(Transaction Processing System),ERP系统。
 
2)Resource Adapter
资源适配器(Resource Adaper)是JCA的关键。要想把不同的EIS整合(或者连接)到J2EE运行环境中,就必须为每个EIS提供资源适配器,它会将将EIS适配为一个具备统一编程接口的资源 (Resource) 。这个统一编程接口就是上图中的System Contracts和Client API。下面的UML类图将完美诠释资源适配器。
 
3)Application Server
应用服务器 (Application Server) 通过System Contracts来管理对EIS的安全、事务、连接等。典型的应用服务器有JBoss、JOnAS、Geronimo、GlassFish等。
 
4)Application Component 
应用组件 (Application Component) ,它封装了应用业务逻辑,像对资源的访问和修改。典型的应用组件就是EJB。
 
更多细节请参见:
Sun Microsystems Inc.J2EE Connector Architecture 1.5

在最后分享一个我自己的后端技术群,群里自己收集了一些Java架构资料,大家可以进群领取,qun号:680075317,也可以进群一起交流,比如遇到技术瓶颈、面试不过的,大家一些交流学习!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值