最近偶参与一个J2EE项目,应用架框是struts+spring! 持久层用hibernate,由于业务需要,业务数据来源来二个不同的数据库数据库是Orcale,版本是9.0.1.0.0。由于采用容器管理事务(CMT),对于Spring,一般普通业务应用我用声明式事务,因为这样让代码清洁一点,只有对于特殊的业务我才用编程式事务,如大批量更新时调用存储过程。对于WebLogic Server,JTA TransactionManager的正式JNDI位置是javax.transaction.TransactionManager。在Spring 的JtaTransactionManager中,该值可被指定为“transactionManagerName”。这启用了使用WebLogic JTA子系统的Spring驱动的事务挂起激活了对PROPAGATION_REQUIRES_NEW和PROPAGATION_NOT_SUPPORTED的支持。对于声明式事务我配置在spring的service层,对于编程式事务则在持久层。
下面说一下我的系统分层:acton==>service==>serviceImpl===>DAO===>DAOImpl,由业务层的实现类调用持久层的DAO接口,为了避免DAO的过度设计而使重用性降低,我这里每一个DAO接口对应操作数据库表,由spring的IOC容器注入,这样在serviceImpl层中可以最大程序的重用DAO。因为由于业务需要一个serviceImpl需要从两个数据库中交换数据。操作DAO可以保证处于同一个事务中......
事务的属性和基本概念
Required:
如果在一个事务中调用,就把该方法加到此事务中来,如果还没有启动事务,就启动一个新事务。
RequiredNew:
不管当前有没有事务,都会启动一个新事务,如果当前有事务,会被挂起直到方法结束。
NotSupported:
不能在事务中执行此方法。如果有事务,将会被挂起直到方法结束。
Supports:
如果当前有事务,此方法会加到当前事务,如果没有,容器也不会启动新事务。
Mandatory:
必须在事务中调用此方法,否则抛出异常:TransactionRequiredException。
Never:
必须不在事务中调用此方法,否则抛出RemoteException(远程调用)或EJBException(本地调用)
以下FAQ来自http://dev2dev.bea.com.cn
Q:XA 的Driver 和普通的Driver 有什么区别呢?
A: XA 的Driver 支持分布式的事务处理,这是与non xa driver 的最大区别;JDBC2.0 规范提供了进行分布式事务的能力。分布式事务是个单独的事务,可以应用在位于分离服务器上的多个异构数据库。为了支持分布式事务, JDBC2.0 提供了两个新的接口:javax.sql.XADataSource 和javax.sql.XAConnection。从性能的考虑来说,使用XA 的DRIVER
会比普通的DRIVER 慢。
Q: XA 的连接池,选择了支持本地事务后,是否还支持对全局的操作?
A: 8.1 既支持TX,也支持Non-Tx 的,Weblogic 对所有的已知数据库进行了包装(Wrapper),使接口一致,然而对不同的数据库,根据选择的驱动程序不同,都作了相应的优化和处理。建立数据源时,如果选择Non-Tx 类型,那么它参与到JTA 事务中也不会出错,因为Weblogic 会启动本地事务,但是前提是必须有一个资源(XAResource)参与到事务中(多个资源的话必须打开模拟两
阶段提交协议,不然会出错),但是选择Tx 类型的就必须有一个全局事务,除非选择支持本地事务(SupportsLocalTransaction=true)。另外如果使用了本地事务,就必须设置autocommit 为true,或者手工commit/rollback。
Q: 资料上讲distributed transaction 只能用JTA 的XAResource 来控制事务,如JMS 要参加分布式事务的话就必须用XA 的。但是,我记得在我们的应用中如果在一个容器管理的事务中修改两个不同的数据库(这两个库在一台机器的同一个实例中),那么必须用XA 的driver,难道这也是一种distribued transaction 吗?(它们是同一种资源,都是JDBC,只是跨库而已,为什么属于分布式事务呢?)
A: 分布式事务分事务管理器,资源管理器,应用程序,以及底层事务通讯管理.在xa 中一个db就是一个资源.一个jms 也可以看作是一个资源,事务管理器就是协调这些资源进行统一commit 或者rollback 的,所以来说跨库就属于分布式事务了。
具体操作步骤:
1、Configure a new JDBC Connection Pool:先在weblogic console中配置连接池,这里用
Oracle's Deriver(Thin XA) Versions:8.1.7.9.0.1.9.2.0, 并把Supports Local Transaction勾上。
2、Configure a new JDBC Data Source:配置一个数据源,并绑定上面创建的连接池。
3、在spring的ApplicationContext.xml中配置:
2 < bean id = " myDataSource1 " class = " org.springframework.jndi.JndiObjectFactoryBean " >
3 < property name = " jndiName " >
4 < value > ds1 </ value >
5 </ property >
6 </ bean >
7
8 < bean id = " myDataSource2 " class = " org.springframework.jndi.JndiObjectFactoryBean " >
9 < property name = " jndiName " >
10 < value > ds2 </ value >
11 </ property >
12 </ bean >
13
14 Jta:
15 < bean id = " myTransactionManager " class = " org.springframework.transaction.jta.JtaTransactionManager " />
16
17 SessionFactory:
18 < bean id = " mySessionFactory1 " class = " org.springframework.orm.hibernate.LocalSessionFactoryBean " >
19 < property name = " mappingResources " >
20 < list >
21 < value > myOne.hbm.xml </ value >
22 < value > myTwo.hbm.xml </ value >
23 </ list >
24 </ property >
25 < property name = " hibernateProperties " >
26 < props >
27 < prop key = " hibernate.dialect " > net.sf.hibernate.dialect.OracleDialect </ prop >
28 < prop key = " hibernate.show_sql " > true </ prop >
29 < prop key = " hibernate.jdbc.fetch_size " > 50 </ prop >
30 < prop key = " hibernate.jdbc.batch_size " > 0 </ prop >
31 </ props >
32 </ property >
33 < property name = " dataSource " >
34 < ref bean = " myDataSource1 " />
35 </ property >
36 </ bean >
37
38 < bean id = " mySessionFactory2 " class = " org.springframework.orm.hibernate.LocalSessionFactoryBean " >
39 < property name = " mappingResources " >
40 < list >
41 < value > youOne.hbm.xml </ value >
42 < value > youTwo.hbm.xml </ value >
43 </ list >
44 </ property >
45 < property name = " hibernateProperties " >
46 < props >
47 < prop key = " hibernate.dialect " > net.sf.hibernate.dialect.OracleDialect </ prop >
48 < prop key = " hibernate.show_sql " > true </ prop >
49 < prop key = " hibernate.jdbc.fetch_size " > 50 </ prop >
50 < prop key = " hibernate.jdbc.batch_size " > 0 </ prop >
51 </ props >
52 </ property >
53 < property name = " dataSource " >
54 < ref bean = " myDataSource2 " />
55 </ property >
56 </ bean >
57
58 TranscationManager
59 < bean id = " TransactionProxyFactoryBean " abstract = " true " lazy - init = " true " class = " org.springframework.transaction.interceptor.TransactionProxyFactoryBean " >
60 < property name = " transactionManager " >< ref bean = " myTransactionManager " /></ property >
61 < property name = " transactionAttributes " >
62 < props >
63 < prop key = " find* " > PROPAGATION_REQUIRED,readonly </ prop >
64 < prop key = " save* " > PROPAGATION_REQUIRED </ prop >
65 < prop key = " update* " > PROPAGATION_REQUIRED </ prop >
66 < prop key = " delete* " > PROPAGATION_REQUIRED </ prop >
67 < prop key = " do* " > PROPAGATION_REQUIRED </ prop >
68 </ props >
69 </ property >
70 </ bean >
71
72 DAO配置
73 < bean id = " serviceImpl " parent = " TransactionProxyFactoryBean " >
74 < property name = " target " >
75 < bean class = " com.hi.services.spring.business.SomeServiceImpl " >
76 < property name = " dao1 " >< ref local = " dao1 " /></ property >
77 < property name = " dao2 " >< ref local = " dao2 " /></ property >
78 < property name = " dao3 " >< ref local = " dao3 " /></ property >
79 </ bean >
80 </ property >
81 </ bean >
82
83 4 修改weblogic服务器你发布的domains文件下的config.xml,加上RollbackLocalTxUponConnClose = " true " ,如果这句话不加上
84 insert * 、delete * 、upate * 无法提交。
85 < JDBCConnectionPool
86 DriverName = " oracle.jdbc.xa.client.OracleXADataSource "
87 Name = " baseXaPool " Password = " {3DES}RBDwPC7KmTq1MGymoUMieg== "
88 Properties = " user=admin " RollbackLocalTxUponConnClose = " true "
89 SupportsLocalTransaction = " true " Targets = " myserver "
90 TestConnectionsOnCreate = " false " TestConnectionsOnRelease = " false "
91 TestConnectionsOnReserve = " false "
92 TestTableName = " SQL SELECT 1 FROM DUAL " URL = " jdbc:oracle:thin:@192.168.0.7:1521:flyjie " />
93 < JDBCConnectionPool
94 DriverName = " oracle.jdbc.xa.client.OracleXADataSource "
95 Name = " applicationXaPool " Password = " {3DES}RBDwPC7KmTq1MGymoUMieg== "
96 Properties = " user=flyjie " RollbackLocalTxUponConnClose = " true "
97 SupportsLocalTransaction = " true " Targets = " myserver "
98 TestConnectionsOnCreate = " false " TestConnectionsOnRelease = " false "
99 TestConnectionsOnReserve = " false "
100 TestTableName = " SQL SELECT 1 FROM DUAL " URL = " jdbc:oracle:thin:@192.168.0.55:1521:application " />
101
102 一个例子:
103 public class SomeServiceImpl implements ISomeService {
104
105 private IDao1 dao1; //操作mySessionFactory1的表
106 private IDao2 dao2; //操作mySessionFactory2的表
107 private IDao3 dao3; //操作mySessionFactory1的表
108 //.get
109
110 ///set
111 public Object doIn(Object[] object){
112 String sql = (String)object[1];
113 List resultList = (ArrayList)getIDao1().findPeronList(sql);
114 //..某些业务操作
115 for(.)
116 getIDao2().save(resultList.get(i));
117 //
118 update(object);
119 }
120
121 public void update(Object object){
122 getIDao3().update(//);
123 }
124 }
注意的问题:由于配置不当引起的serive层中的事务嵌套,这一个人很恼人的问题。我们在配置声明式事务时一般是以service层中的方法名来作来启动事务的标志。如save*,find*,你的serviceImpl中有save方法或findList方法,
spring会在自动把在该方法内操作的资源纳入到同一个事务中,以保证数据ACID(Atomicity、Consistency、Isolation、Durability)。