Spring全局事务之JBOSS JTA实现Oracle、Ms SqlServer在同一事务中
很久以来就一直知道,使用容器事务可以实现多个不同数据源处于同一事务中的功能。苦于没有时间和机会好好实验一把。今天机会难得,化了半天时间做了一个两个Oracle数据源和一个Ms Sql Server数据源在JBoss下的协同事务。下面简单说一下配置和实验情况。
第一、JBoss数据源配置
要使用JBoss的JTA,首先要做的就是配置链接各种数据源的XA类型的ds文件。
1、链接Oracle一的数据源,这是一个链接Oracle小型机的oracle-ds.xml 链接文件。配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<!-- ===================================================================== -->
<!-- -->
<!-- JBoss Server Configuration -->
<!-- -->
<!-- ===================================================================== -->
<!-- $Id: oracle-xa-ds.xml 63175 2007-05-21 16:26:06Z rrajesh $ -->
<!-- ===================================================================== -->
<!-- ATTENTION: DO NOT FORGET TO SET Pad=true IN transaction-service.xml -->
<!-- ===================================================================== -->
<datasources>
<xa-datasource>
<jndi-name>XAOracleDS</jndi-name>
<track-connection-by-tx/>
<isSameRM-override-value>false</isSameRM-override-value>
<xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>
<xa-datasource-property name="URL">jdbc:oracle:thin:@(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.10.243)(PORT = 1521))(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.10.244)(PORT = 1521))(LOAD_BALANCE = yes)(CONNECT_DATA =(SERVER = DEDICATED)(SERVICE_NAME = GBDATA)))</xa-datasource-property>
<xa-datasource-property name="User">abc</xa-datasource-property>
<xa-datasource-property name="Password">abc</xa-datasource-property>
<!-- Uses the pingDatabase method to check a connection is still valid before handing it out from the pool -->
<!--valid-connection-checker-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleValidConnectionChecker</valid-connection-checker-class-name-->
<!-- Checks the Oracle error codes and messages for fatal errors -->
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter</exception-sorter-class-name>
<!-- Oracles XA datasource cannot reuse a connection outside a transaction once enlisted in a global transaction and vice-versa -->
<no-tx-separate-pools/>
<!-- corresponding type-mapping in the standardjbosscmp-jdbc.xml -->
<metadata>
<type-mapping>Oracle9i</type-mapping>
</metadata>
</xa-datasource>
<mbean code="org.jboss.resource.adapter.jdbc.vendor.OracleXAExceptionFormatter"
name="jboss.jca:service="OracleXAExceptionFormatter">
<depends optional-attribute-name="TransactionManagerService">jboss:service=TransactionManager</depends>
</mbean>
</datasources>
2、链接Oracle二的数据源,这是一个链接Oracle测试服务器的testoracle-xa-ds.xml链接文件。配置如下
<?xml version="1.0" encoding="UTF-8"?>
<!-- ===================================================================== -->
<!-- -->
<!-- JBoss Server Configuration -->
<!-- -->
<!-- ===================================================================== -->
<!-- $Id: oracle-xa-ds.xml 63175 2007-05-21 16:26:06Z rrajesh $ -->
<!-- ===================================================================== -->
<!-- ATTENTION: DO NOT FORGET TO SET Pad=true IN transaction-service.xml -->
<!-- ===================================================================== -->
<datasources>
<xa-datasource>
<jndi-name>XAOracleDSTest</jndi-name>
<track-connection-by-tx/>
<isSameRM-override-value>false</isSameRM-override-value>
<xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>
<xa-datasource-property name="URL">jdbc:oracle:thin:@(DESCRIPTION =(ADDRESS_LIST =(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.10.48)(PORT = 1521)))(CONNECT_DATA =(SID = testdb)))</xa-datasource-property>
<xa-datasource-property name="User">aaa</xa-datasource-property>
<xa-datasource-property name="Password">aaa</xa-datasource-property>
<!-- Uses the pingDatabase method to check a connection is still valid before handing it out from the pool -->
<!--valid-connection-checker-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleValidConnectionChecker</valid-connection-checker-class-name-->
<!-- Checks the Oracle error codes and messages for fatal errors -->
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter</exception-sorter-class-name>
<!-- Oracles XA datasource cannot reuse a connection outside a transaction once enlisted in a global transaction and vice-versa -->
<no-tx-separate-pools/>
<!-- corresponding type-mapping in the standardjbosscmp-jdbc.xml -->
<metadata>
<type-mapping>Oracle9i</type-mapping>
</metadata>
</xa-datasource>
<mbean code="org.jboss.resource.adapter.jdbc.vendor.OracleXAExceptionFormatter"
name="jboss.jca:service=OracleXAExceptionFormatterTest">
<depends optional-attribute-name="TransactionManagerService">jboss:service=TransactionManager</depends>
</mbean>
</datasources>
3、链接Sql Server的数据源,mssql-xa-ds.xml配置如下
<?xml version="1.0" encoding="UTF-8"?>
<!-- ===================================================================== -->
<!-- -->
<!-- JBoss Server Configuration -->
<!-- -->
<!-- ===================================================================== -->
<!-- $Id: mssql-xa-ds.xml 63175 2007-05-21 16:26:06Z rrajesh $ -->
<!-- ==================================================================== -->
<!-- ConnectionManager setup for xa Microsoft SQL Server 2005, using -->
<!-- Microsoft's JDBC driver. -->
<!-- Thanks to Benjamin Geer <[email protected]> -->
<!-- Be sure to set the JndiName property to the name you want to look up -->
<!-- the datasource under and set the location of your database in -->
<!-- the xa-datasource-property section. -->
<!-- Further information about the Microsoft JDBC Driver version 1.1 -->
<!-- can be found here: -->
<!-- http://msdn2.microsoft.com/en-us/library/aa496082.aspx -->
<!-- ==================================================================== -->
<datasources>
<xa-datasource>
<jndi-name>MSSQLXADS</jndi-name>
<track-connection-by-tx/>
<isSameRM-override-value>false</isSameRM-override-value>
<xa-datasource-class>com.microsoft.sqlserver.jdbc.SQLServerXADataSource</xa-datasource-class>
<xa-datasource-property name="ServerName">192.168.10.15</xa-datasource-property>
<xa-datasource-property name="DatabaseName">TestData</xa-datasource-property>
<xa-datasource-property name="SelectMethod">cursor</xa-datasource-property>
<!-- not sure if these should be here-->
<user-name>SM</user-name>
<password>123123</password>
<!-- corresponding type-mapping in the standardjbosscmp-jdbc.xml -->
<metadata>
<type-mapping>MS SQLSERVER2000</type-mapping>
</metadata>
</xa-datasource>
</datasources>
配置好三个数据源后,放到JBoss目录下:D:/jboss-4.2.2.GA/server/default/deploy
第二、各种数据源的驱动配置。
链接Oracle 和Sql Server的驱动,需要放到JBoss的Lib目录中
Oracle的驱动如下:
ojdbc14.jar、ojdbc14_g.jar
Sql Server的驱动如下:
msbase.jar、mssqlserver.jar、msutil.jar、sqljdbc.jar(Microsoft SQL Server 2005 JDBC Driver,在此Jar中包含了XA事务所必须的com.microsoft.sqlserver.jdbc.SQLServerXADataSource.class)
要使用Sql Server2005的XA事务,还需要满足以下条件:
1、Sql Server2005运行在Window2000或者Window2003环境下
2、在数据库服务器上运行MSDTC,并且启用XA事务。(在组件服务-->我的电脑,右键属性,可以看到MSDTCx项目)
3、在数据库的Master库中创建扩展存储过程、根据机器的不同把sqljdbc_xa.dll文件拷贝到sql server的Binn目录下,并在数据库中设置相应权限,详细过程如下:
1. 将 sqljdbc_xa.dll 从该目录复制到将参与分布式事务的每台 SQL Server 计算机的 Binn 目录下。
注意:
如果使用 32 位处理器,请使用文件夹 x86 中的 sqljdbc_xa.dll。
如果使用 64 位 x64 处理器,请使用文件夹 x64 中的 sqljdbc_xa.dll。
2. 在将要参与分布式事务的每台 SQL Server 计算机上执行数据库脚本 xa_install.sql。
3. 要授予特定用户
使用 Microsoft SQL Server 2005 JDBC 驱动程序参与分布式事务的权限,请将该用户添加到 SqlJDBCXAUser 角色中。使用什么用户连接Sql Server就需要把该用户加入到SqlJDBCXAUser 角色中
第三、在JBOSS的EJB中使用Spring
在EJB中使用单例模型创建ApplicationContext对象实例
public final class AppBeanFactory {
private static ApplicationContext factory=null;
private AppBeanFactory() {}
public static synchronized ApplicationContext getInstance() {
if(factory == null) {
factory=new ClassPathXmlApplicationContext("conf/applicationContext.xml");
}
}
}
applicationContext.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.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean id="wmsdstestjndi" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:XAOracleDSTest"></property>
</bean>
<!-- JNDI获取真实数据源 -->
<bean id="wmsdsjndi" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:XAOracleDS"></property>
</bean>
<!-- JNDI获取Sql Server数据源 -->
<bean id="sqldsjndi" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:MSSQLXADS"></property>
</bean>
<!-- 定义事务管理器 -->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 定义各个数据源对应的jdbcTemplace -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource">
<ref bean="wmsdstestjndi" />
</property>
</bean>
<bean id="jdbcTemplateebdata" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource">
<ref bean="wmsdsjndi" />
</property>
</bean>
<bean id="jdbcTemplatesql" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource">
<ref bean="sqldsjndi" />
</property>
</bean>
</beans>
在具体的EJB中使用如下样式调用:
public class OrderForm implements IOrderForm{
@Transactional(readOnly=false, propagation = Propagation.REQUIRED,rollbackFor=Exception.class)
public void InsertOrderForm() throws Exception {
//Oracle一、
JdbcTemplate myjdbc=(JdbcTemplate)AppBeanFactory.getInstance().getBean("jdbcTemplate");
this.jdbc.execute("Insert Into OrderFormTest(OrderFormID,FormDate)values('ODZB001',to_date('2009-11-02','YYYY-MM-DD'))");
//Oracle 二
JdbcTemplate myjdbc=(JdbcTemplate)AppBeanFactory.getInstance().getBean("jdbcTemplateebdata");
myjdbc.execute("Insert into OrderFormSTTest(OrderFormID,ProductID,Qty)values('ODZB001','000001',10)");
//Ms Sql Server
myjdbc=(JdbcTemplate)AppBeanFactory.getInstance().getBean("jdbcTemplatesql");
myjdbc.execute("Insert into OrderFormSTTest(OrderFormID,ProductID,Qty)values('ODZB001','000001',10)");
throw new Exception("Error occured in OrderFormST");
}
}
第四、把OrderForm类封装进EJB中、以便实现远程调用
1、远程接口
@Remote
public interface ShoppingCart {
// 添加某个商品信息
public void addCommodity (String value);
// 获得客户购买的所有商品的纱衔泥
public List<String> getCommodity();
}
2、远程接口实现
@Stateful
public class ShoppingCartBean implements ShoppingCart {
public void addCommodity(String value) {
IOrderForm of=(IOrderForm)AppBeanFactory.getInstance().getBean("OrderForm");
try {
of.InsertOrderForm();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
第五、客户端实现EJB调用
ShoppingCart shoppingCart = (ShoppingCart)ic.lookup("ShoppingCartBean/remote");
shoppingCart.addCommodity("自行车");