先给出通过JBoss容器获取JTA事务的方法
private UserTransaction getUserTransaction() throws ServletException {
UserTransaction ut;
try {
InitialContext ic = new InitialContext();
//comp java:/UserTransaction
ut = (UserTransaction) ic.lookup("java:comp/UserTransaction");
} catch(Exception e) {
throw new ServletException(e);
}
return ut;
}
经测试使用串“java:comp/UserTransaction”从JNDI获取JTA事务。启动后发现抛类型转换异常
org.jboss.tm.usertx.client.ServerVMClientUserTransaction cannot be cast to javax.transaction.UserTransaction
应用中多余引入了jta.jar导致。该jar已由JBoss提供,应用中删除即可
JTA属于分布式事务,如何结合JDBC事务呢,即在JTA事务内,对数据库的操作也具有事务特性。
通常在JBoss容器中使用数据库时,一般会在jboss-4.0.5.GA\server\default\deploy目录下面配置数据源,本地事务配置如下:
<datasources>
<local-tx-datasource>
<jndi-name>java:mysqlDS</jndi-name>
<connection-url>jdbc:mysql://192.168.0.10:1521/test</connection-url>
<driver-class>com.mysql.Driver</driver-class>
<user-name>root</user-name>
<password>root</password>
<min-pool-size>5</min-pool-size>
<max-pool-size>100</max-pool-size>
</local-tx-datasource>
</datasources>
在JBoss应用中通过JNDI获取该数据源时,JTA中对数据库的操作是不能回滚的,需要配置成分布式事务的形式,即:
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<xa-datasource>
<jndi-name>mySQLDataSource</jndi-name>
<track-connection-by-tx>true</track-connection-by-tx>
<xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
<new-connection-sql>set autocommit=1</new-connection-sql>
<no-tx-separate-pools>true</no-tx-separate-pools>
<xa-datasource-property name="Url">jdbc:mysql://127.0.0.1:3306/mytest?autoReconnect=true</xa-datasource-property>
<xa-datasource-property name="User">root</xa-datasource-property>
<xa-datasource-property name="Password">root</xa-datasource-property>
<transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
<exception-sorter-class-name>
org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter
</exception-sorter-class-name>
<type-mapping>mySQL</type-mapping>
<!--pooling parameters-->
<min-pool-size>5</min-pool-size>
<max-pool-size>100</max-pool-size>
<blocking-timeout-millis>5000</blocking-timeout-millis>
<idle-timeout-minutes>15</idle-timeout-minutes>
</xa-datasource>
</datasources>
转自:http://www.jdon.com/jivejdon/thread/30264 的一段说明。
为了支持I2SS框架,目前的解决方式就是替换支持分布式事务的驱动,修改数据源配置将其替换成<xa-datasource>。
配置文件如下:
<datasources>
<xa-datasource>
<jndi-name>DataSource</jndi-name>
<track-connection-by-tx>true</track-connection-by-tx>
<xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
<new-connection-sql>set autocommit=1</new-connection-sql>
<no-tx-separate-pools>true</no-tx-separate-pools>
<xa-datasource-property name="Url">jdbc:mysql://127.0.0.1:3306/sms1?autoReconnect=true</xa-datasource-property>
<xa-datasource-property name="User">smsdb</xa-datasource-property>
<xa-datasource-property name="Password">smsdb</xa-datasource-property>
<transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
<exception-sorter-class-name>
org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter
</exception-sorter-class-name>
<type-mapping>mySQL</type-mapping>
<!--pooling parameters-->
<min-pool-size>5</min-pool-size>
<max-pool-size>100</max-pool-size>
<blocking-timeout-millis>5000</blocking-timeout-millis>
<idle-timeout-minutes>15</idle-timeout-minutes>
</xa-datasource>
</datasources>
在上述配置文件中用户色标记出的元素需要特别注意,而且这四个配置元素必须同时使用。下面将进行详细解释:
1、<no-tx-separate-pools>
这个元素的设定使得两个连接池能够分开:一个支持JTA事务的连接,另一个不支持JTA事务的连接。连接池在第一次
使用时创建。
2、<track-connection-by-tx>
当这个值设置为true时,连接管理器会保持一个XID-to-connection影射表,当事务结束时而且所有的连接关闭或者
未被引用时连接将放回连接池。这样做的副作用是不会挂起或者恢复连接的XAResource里的XID,这个行为和本地事务
是一样的。
3、<xa-datasource-class>
指定javax.sql.XASDataSource实现类的全名
4、<new-connection-sql>
当新的连接创建时执行此语句。
在我们的配置文件中,我们进行了如下配置:
<new-connection-sql> set autocommit=1</new-connection-sql>
这样做的原因是因为我们通过MySQLXADataSource中获取的连接默认被设置成autocommit 为false,如果我们的连接
并入到事务中时(即事务启动之后获取的连接),会统一由UserTransaction 进行提交。对于那些没有并入事务中的单连接,
就会出现操作一直不能提交的错误,可能出现XAER_OUTSIDE 异常。通过上述的设置,我们可以将单连接操作放入一个
隔离的连接池中,并将其设置为自动提交。
【避免此类问题的建议】
产生这个问题的原因可以归结为I2SS框架中事务处理不当,connection 封装不合理。为了适应框架改用分布式事务,
理论上讲会造成非常大的性能开销。
注意需要在JBoss中引入支持XA的数据库驱动包
获取数据源和数据库连接的方式为:
//获取数据源:
InitialContext ic = new InitialContext();
ds = (DataSource) ic.lookup("java:mySQLDataSource");
//获取连接
Connection conn = ds.getConnection();
一个事务操作实例如下:
try {
ut.begin();
Admin a = new Admin();
a.setId(2);
adminDAO.delAdmin(a);
System.out.println("before add:" + cache.get("KEY-10"));
int index = serviceWithinTx(servletResponse, cache);
System.out.println("after add:" + cache.get("KEY-10"));
printLine(servletResponse, "Bye #" + (index + 1));
ut.commit();
} catch (Exception e) {
printLine(servletResponse, "Caught a " + e.getClass() + "! Rolling Tx back");
if (!printStackTrace) {
PrintWriter s = servletResponse.getWriter();
e.printStackTrace(s);
s.flush();
}
rollbackTransaction(ut);
}
OK!在JBoss容器环境中,利用JTA分布式事务特性将应用中事务(如:Ehcache中支持获取JBoss JTA事务管理器)与数据库事务(即JDBC事务,包括多JBDC事务)组合起来的实现成功了!