在Tomcat中通过JOTM支持JTA

因为需要将项目从IBM WebSphere Application Server移植到Tomcat上开发,所以研究了一下在Tomcat中通过JNDI查找和使用JDBC及JTA的方法。

Tomcat 是Servlet容器,但它也提供了一个JNDI InitialContext实现,因此用户可以像在J2EE应用程序服务器中一样在Tomcat中使用JNDI查找JDBC数据源。不过在事务处理方面,Tomcat本身并不支持JTA(Java Transaction API),所以需要借助其他的方案。
JOTM(Java Open Transaction Manager)是ObjectWeb的一个开源JTA实现,它本身也是开源应用程序服务器JOnAS(Java Open Application Server)的一部分,为其提供JTA支持和分布式事务管理。JOTM同样可以为Tomcat提供JTA支持,以下将对相关的配置进行简单说明,使用的相应版本为:

Tomcat 5.5.x
JOTM 2.0.x
Oracle 9i

1. 配置Tomcat环境

在$TOMCAT_HOME/conf/context.xml文件中添加以下内容:

<Resource name="jdbc/framework" auth="Container"
type="javax.sql.DataSource"
factory="org.objectweb.jndi.DataSourceFactory"
username="user" password="pwd"
driverClassName="oracle.jdbc.driver.OracleDriver"
url="jdbc:oracle:thin:@172.18.136.200:1521:ORADB"
maxActive="30" maxIdle="30"/>

<Transaction factory="org.objectweb.jotm.UserTransactionFactory"
jotm.timeout="60"/>

2. 添加所需的JAR文件

下载JOTM,将以下文件添加到$TOMCAT_HOME/common/lib/:
jotm.jar
jotm_jrmp_stubs.jar
jotm_iiop_stubs.jar
ow_carol.jar
jta-spec1_0_1.jar
jts1_0.jar
objectweb-datasource.jar
xapool.jar
howl.jar
connector-1_5.jar
同时,还需要添加相应数据库的JDBC包,例如Oracle的classes12.jar

3. 配置JOTM

新建一个carol.properties文件,置于$TOMCAT_HOME/common/classes/,文件内容如下:

# JNDI (Protocol Invocation)
carol.protocols=jrmp

# Local RMI Invocation
carol.jvm.rmi.local.call=true

# do not use CAROL JNDI wrapper
carol.start.jndi=false

# do not start a name server
carol.start.ns=false

# Naming Factory
carol.jndi.java.naming.factory.url.pkgs=org.apache.naming

这样JOTM将不会使用CAROL JNDI wrapper,从而可以避免类装载错误的发生

4. 说明

4.1 JOTM目前的版本在JDK1.5或以上可能无法正常运行,解决的方法有两个:使用JDK1.5重新编译carol库,或者将Tomcat运行在JDK1.4中

4.2 <Transaction>是Tomcat 5中的新标记,对于不支持此标记的老版本,需要使用以下语句代替事务资源的声明:

<!-- Resource configuration for UserTransaction
use JOTM
-->
<Resource name="UserTransaction" auth="Container"
type="javax.transaction.UserTransaction"
factory = "org.objectweb.jotm.UserTransactionFactory"
jotm.timeout = "60"/>

4.3 需要注意的是,使用<Resource>节点声明的资源默认上下文前缀是"java:comp/env",而使用< Transaction>节点时则是"java:comp"。因此,当使用4.2的方式声明用户事务时,相应的JNDI查找代码也应该改为 UserTransaction ut = (UserTransaction)initCtx.lookup("java:comp/env/UserTransaction");

5. 测试
假设数据库中已经做了相应配置,可以使用如下jsp页面进行测试:
<!--test.jsp-->
<%@page contentType="text/html;charset=GB2312"%>
<%@page import="java.sql.*"%>
<%@page import="javax.sql.*"%>
<%@page import="javax.naming.*"%>
<%@page import="javax.transaction.UserTransaction"%>
<%
ResultSet rs = null;
Statement stmt = null;
UserTransaction ut = null;
Connection conn = null;
try {
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource ds = (DataSource) envCtx.lookup("jdbc/framework");
ut = (UserTransaction)initCtx.lookup("java:comp/UserTransaction");
conn = ds.getConnection();

ut.begin();
System.out.println("<<< beginning the transaction >>>");

stmt = conn.createStatement(
// ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE
);
rs = stmt.executeQuery("SELECT PRICE FROM TM_PRODUCT WHERE ID=1");
rs.next();
} catch(Exception e) {e.printStackTrace();}
%>
<html>
<body>
Original price:
<%=rs.getString("price")%>
<br>After update:
<%
PreparedStatement pstmt = conn.prepareStatement("update tm_product set price=? where id=1");
pstmt.setInt(1,101);
pstmt.executeUpdate();
rs = stmt.executeQuery("SELECT PRICE FROM TM_PRODUCT WHERE ID=1");
rs.next();
%>
<%=rs.getString("price")%>
<br>After Rollback:
<%
System.out.println("<<< rolling back the transaction >>>");
ut.rollback();//Or ut.commit();
rs = stmt.executeQuery("SELECT PRICE FROM TM_PRODUCT WHERE ID=1");
rs.next();
%>
<%=rs.getString("price")%>
<%conn.close();%>
</body>
</html>

6. 参考
How to use JDBC and transactions in Tomcat with JOTM
How to use JDBC and transactions in Tomcat with JOTM(For Tomcat 5.5.x)
UserTransaction, JOTM and Tomcat 5.5.x


国内最棒的Google Android技术社区(eoeandroid),欢迎访问!

《银河系列原创教程》发布

《Java Web开发速学宝典》出版,欢迎定购

上一篇:《程序员》第9期智慧擂台题目——高频词汇提取


下一篇:uni-app的基本使用