在我的以前的
blog
中曾多次谈到了跨数据库的事务(即分布式事务),的确这个方面可能在平时的应用中遇到的不多,但在一些比较大型的项目中或是多系统合作的条件下还是会碰到的。
在
J2EE
下这个问题的解决有两种方案:一是采用
EJB
,利用容器去管理,我们把针对多个数据库的操作方法放到
session bean
的一个方法中,当和数据库交互产生问题时抛出
EJBException
,通知容器,则可以做到整个事务的回滚,在这里要注意一点的是我们的数据源配置要用
XA
的数据源配置
(
具体的可以看
Jboss
中的例子
)
,不过你用普通的数据源也可以做到回滚,但容器在装载应用程序和
EJB
时会给出警告,说明在可能的情况下不能做到事务的整个回滚,所以我们还是用
XA
的数据源比较稳妥。
另一种方法是不用
EJB
容器的自动管理,自己在代码中手动调用
JTA
的
API
来实现分布式事务的管理,要能这样做,首先你使用的
J2EE
服务器要提供
JTA
的实现,数据库和数据库驱动程序都要支持
XA
的事务。我在实践中测试用的是
JBoss4.0.2
,
SQL Server2000
,
jtds 1.2
的组合,没有问题。
首先我们要找到容器提供的事务管理器,
InitialContext jndiContext = new InitialContext();
UserTransaction utx = (javax.transaction.UserTransaction) jndiContext.lookup(
"java:comp/UserTransaction");
当程序找到
UserTransaction
后就可以开始分布式事务了。
try{
utx.begin()
……… //
针对多个数据库的操作
utx.commit();
}
}
catch(Exception e){
utx.rollback();
utx.rollback();
}
以上就是通过程序来实现分布式的事务处理。
在这个有几个细节要注意:
一是我们在事务中要处理的数据库的连接的建立要放在
utx.begin( )
之后,不能建立在事务开始之前,否则事务是会失效的(这个也很好理解,在我上篇
blog
中讲的
XA
事务的实现逻辑中就能找到答案);但特别的是数据库的连接建立要在事务开始之后,但其关闭却不用在事务结束之前,也就是说我在事务中建立的
conn
,可以在事务完成后仍保持连接继续使用,而不是像有些资料所说事务的生命周期一定要长于
conn
的生命周期。
另外一点要注意的是在
JTA
的事务中不能再有
JDBC
的事务,比如在上面
utx.begin( )
之后,我对一个数据库的操作有多起,那这几起中我再使用
JDBC
的事务,如
conn.commit( ),conn.rollback( )
等,这是不行的,程序会报错。但当我的
JTA
的事务处理完后,也就是
utx.commit( )
后再对某个
conn
的操作,我仍可以使用
JDBC
的事务了。也就是说实际上一个XA的连接(XA数据源产出的连接)我完全能将其看作是一个非XA的连接(普通的数据源产生的连接),但是返过来却不能,非XA的连接不能参与JTA的事务(至少会给出警告)!