IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope:Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Open source | Java technology  >

Geronimo 应用服务器中的事务

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

讨论

样例代码


级别: 中级

Neal Sanche (neal@nsdev.org), Java 开发人员, Pure Technologies

2005 年 11 月 21 日

事务是每个 Java™ 2 平台企业版 (J2EE) 应用程序不可或缺的一部分,您访问数据库的时候经常要用到事务。事务对于维护数据完整性是至关重要的。Java 开发人员和定期撰稿人 Neal Sanche 描述了 J2EE 事务的实质以及如何在 Apache Geronimo 应用服务器中使用它们。为了演示事务,您将使用一个简单的示例程序 Transaction Demo,它具有一个简单菜单允许您与一个关于星星名称的数据库进行交互。

事务概述

几乎编写所有的 Geroimo 应用程序时,您都需要建立和处理事务。在很多情况下,例如操纵数据库记录、操作容器管理关系 (CMR)、发送 Java 消息服务 (JMS) 消息时,Geronimo 都要求您必须使用事务,否则就会出错返回。

已定义的事务

事务 是指一个原子的动作集合,当执行过程中发生错误时,可以取消所有的动作,恢复原来的状态。当其中的任何一个动作出错,所有的动作都不执行。

容器管理关系 (CMR) 是一个 J2EE 概念,它能够提供对一系列实体及实体间关系的自动持久化。例如,一个订单记录可能包含多行货物。这应该由 Geronimo 容器自动地处理,以便所有的 SQL 语句都在后台执行。

容器管理事务 (CMT) 是 Geronimo 容器自动提供的一个事务,它由容器进行管理,不需要编写代码对它进行处理。

利用容器管理的持久性 (CMP),Enterprise JavaBeans™ (EJB) 容器自动管理持久性状态。CMP 能够使用较少的代码提供更好的性能。

为了更加熟悉在 Geronimo 应用程序中使用事务的重要细节,您将使用一个叫做 Transaction Demo 的小应用程序,创建它是为了部署在 Geronimo 应用服务器。程序基于 Geronimo M5(第五个重要版本)的早期版本进行开发。(请参阅 参考资料,链接到 Apache Software Foundation 站点下载最新的 Geronimo 应用服务器版本。)

只要数据库在 CMP 期间被访问,J2EE 应用程序就要使用事务。如果在创建、更新或者删除一个实体 bean 时发生错误,那么可以还原到数据库的前一状态并且通知用户存在问题。这里主要考虑的问题是数据完整性。事务通过撤销所有潜在的不完整操作来保护数据库中的数据完整性。

在 J2EE 应用程序中,您可以通过获得和使用 UserTransaction 类的实例在自己的代码中创建事务和管理它的生命周期(请参阅 直接删除 Sol 一节),也可以通过使用 CMT 依靠 EJB 容器来为您创建和管理事务。您会得到通过 Geronimo 应用服务器使用这两种方法的细节。

前面已经提到,事务不仅仅用于数据库。J2EE 规范中关键的部分描述了事务在 Java 消息服务 (JMS) 中的使用。JMS 是消息驱动 beans (MDB) 的重要组成部分并且可以在事务中使用,以便那些因为某种异常而没有被传递的消息会使事务回滚,导致消息重新被传递。Geronimo 嵌入了 ActiveMQ JMS 服务,所以您可以试着创建一个 MDB。您甚至可以使用 Geronimo 控制台开始 SystemJMS 配置以及配置 JMS 服务器。但是这已经超出了本文的范围。(请参阅 参考资料 获得这些主题的更多信息。)

本文介绍了事务的使用,包括在 Web 应用程序中使用事务,以及通过利用 CMT 的无状态会话 bean 实例使用事务。本文也展示了一些从失败的事务中完好地恢复的例子。





回页首


示例程序

本文中使用 Transaction Demo 这个简单的示例程序来演示事务,它是一个 Struts 应用程序,具有必须部署在 Geronimo 应用服务器中的所有部署计划。它提供一个简单的菜单允许您与一个基本的 星星(指的是太阳和其他的天体,不是好莱坞电影明星)数据库进行交互。此外,它还演示了在您的 Geronimo 程序中使用事务的许多重要概念。

要运行 Transaction Demo 程序,必须首先从源代码构建 Geronimo 应用服务器。只有当许多 Geronimo 构建产品在构建期间都可用时,针对示例程序的 Maven 构建脚本才能完成。其次,您还需要从 sourceforge.net (请参阅 参考资料 )下载 XDoclet 1.2.3,并将所有缺少的二进制文件安装到您的 Maven 库中。(当 IBiblio Maven 库包含所有的 XDoclet 1.2.3 二进制文件时,这一步可以省略。在撰写本文时,库中至少缺少一个二进制文件。)

正确地完成构建后,您会在结果目标目录下找到一个 transactiondemo-ejb.sql 文件。这是应用程序的 SQL 模式。如果完成接下来的几个步骤,您就可以将这个模式安装到 Geronimo 的 Apache Derby 内置数据库中。

首先,使用如下命令行启动您的 Geronimo 服务器:

java -jar server.jar

这将启动服务器和新的 Geronimo 控制台,控制台管理您的服务器和用于部署应用程序的一系列其他 Geronimo 服务。(注意:只有在使用 M5 版本的 Geronimo 时才是这样的,以前的版本只能启动一个最小的服务器,并且部署到该服务器需要启动更多的服务。)

接着,通过转到 http://localhost:8080/console,使用 Web 浏览器连接到控制台。您可以看到控制台欢迎界面,如 图 1 所示。使用默认的用户名 system 和密码 manager 登录。


图 1. Geronimo 控制台欢迎界面
Geronimo 控制台欢迎界面

登录以后,使用左侧的导航条进行导航,并选择 All Configurations。来看一下 Installed Applications 列表,确保您已经通过点击它旁边的 Start 链接启动了(或者看到它在正在运行)org/apache/geronimo/SystemDatabase 配置。这将启动 Apache Derby 数据库引擎,这样您就可以对它进行配置了(请参阅 图 2)。在最低限度下,要编译并运行示例应用程序,您至少需要启动了下面这些配置:

org/apache/geronimo/RuntimeDeployer
org/apache/geronimo/JettyRuntimeDeployer
org/apache/geronimo/SystemDatabase


图 2. 启动了系统数据库的 All Configurations
启动了系统数据库的 All Configurations

接下来,滚动到导航条底部,选择 DB Manager 链接。该 portlet(控制台开发人员是这样叫的)提供了一个简单的方法,以针对系统数据库执行 SQL 查询和创建新的数据库。将 SQL 从 transactiondemo-ejb.sql 文件(不包含 drop table 语句)输入到 SQL Command 文本区,点击 Run SQL 按钮,如 图 3 所示。


图 3. 使用 DB Manager 安装应用程序数据库模式
使用 DB Manager 安装应用程序数据库模式

这将创建示例应用程序所需的表。您可以通过点击 DB Manager portlet 的 Database List 部分中 View Table 选项下的 Application 链接来查看数据库中的这些表。当然,此时推荐对 Geronimo 控制台的其他重要特性进行探索。现在,如果已经在源目录中运行了 Maven 命令,则会安装 transactiondemo 应用程序。您可以通过转向 URL http://localhost:8080/trasactiondemo 来访问该程序,您可以看到屏幕显示如 图 4 所示。


图 4. Transaction Demo 应用程序界面
Transaction Demo 应用程序界面

屏幕左侧的菜单链接以多种方式调用数据库事务代码。这些菜单项的含义如下,但是文章的后面还会给出关于每个菜单项功能的更详细的介绍。

  • Clear 清空 Stars 数据库表。
  • Add Records with Failure 添加几条记录,但是在完成前方法抛出了异常,所以 CMT 自动回滚。
  • Add Records 添加记录,没有抛出异常。
  • Directly Remove Sol 通过获得一个 UserTransaction 实例并且在 Struts 动作中使用它来演示在 Web 层中处理一个事务。

每个选项的代码都会在本文后面进行介绍。

清空数据库

选择 Clear 菜单项会清空数据库记录,这样您运行示例程序的时候就可以重新开始。为了清空数据库,程序使用一个无状态会话 bean 自动启动 CMT。TransactionDemoSessionBean.java 类包含一个 clear() 方法(请参阅 清单 1),该方法执行所需的实体迭代并删除每一个实体。因为这是作为一个单独的事务执行,所以操作应当相对较快,尽管对于每个 star.remove() 方法来说,在底层上这可能被转换为一个 delete 语句(SQL)。


清单 1. TransactionDemoSessionBean.java clear() 方法
        
/**
 * Perform the first test.
 * @throws NamingException 
 * @throws CreateException 
 * 
 * @ejb.interface-method view-type="both"
 * @ejb.transaction      type="Required"
 */
public void clear() {
   try {
      Collection stars = StarUtil.getLocalHome().findAll();
      Iterator i = stars.iterator();
      while(i.hasNext()) {
         StarLocal star = (StarLocal)i.next();
         star.remove();
      }
   } catch (Throwable ex) {
      ex.printStackTrace();
   }
}

该代码使用 XDoclet 标记来装饰,具体来说是 @ejb.transaction 标记,指出 clear() 方法的入口需要一个事务。如果调用线程还没有与事务关联,那么 XDoclet 标记转换成 ejb-jar.xml 部署描述符中的一个入口(请参阅 清单 2),通知 Geronimo 创建一个 CMT。


清单 2. ejb-jar.xmlclear() 方法的片段
        
   <container-transaction>
      <method>
         <ejb-name>TransactionDemoSession</ejb-name>
         <method-intf>Local</method-intf>
         <method-name>clear</method-name>
         <method-params>
         </method-params>
      </method>
      <trans-attribute>Required</trans-attribute>
   </container-transaction>

CMT 比较好的一点是您不需要关心它们的启动、回滚和提交。容器可以自动地完成这一切。例如,如果在从数据库中删除所有的星星记录时发生某种类型的异常,那么 Geronimo 将启动事务的回滚,在 clear() 方法中不会提交任何删除。下面一节演示这个回滚行为。

添加记录失败

Transaction Demo 应用程序中的 Add Records with Failure 菜单项演示了当操作过程中发生异常时事务将如何处理。就像前面提到过的,如果在一个被标记为具有 CMT 的方法执行过程中发生了异常,那么事务将自动回滚。在 清单 3 中的代码中,在方法结束时总是抛出一个异常,所以强制回滚。


清单 3. test1() 方法,强制异常演示回滚
        
 /**
  * Perform the first test. This test makes an attempt to 
  * add a number of stars, but then throws an exception.
  *
  * @throws NamingException 
  * @throws CreateException 
  * @throws IllegalStateException always
  * 
  * @ejb.interface-method view-type="both"
  * @ejb.transaction      type="Required"
  */
 public void test1() throws CreateException, NamingException, 
     IllegalStateException {
   StarLocalHome home = StarUtil.getLocalHome();
   home.create("Sol");
   home.create("Betelguese");
   home.create("Andromeda");
   throw new 
        IllegalStateException("Pretending that something"+
        " bad happened.");
   }

在方法结尾处总是抛出 IllegalStateException 异常。如果事务起作用,由于异常回滚了所有改动,所以将看不到任何星星 —— Sol、Betelguese 或 Andromeda —— 被创建在数据库中。您可以看到,事实确实是这样。

添加记录

Transaction Demo 应用程序中的 Add Records 菜单项与 Add Records with Failure 菜单项相反。它演示了不发生异常时的情况;也就是说,它将向数据库中插入记录。在 清单 4 的方法中,除了代码不抛出异常的事实外并没有什么太多的事情。所以如果数据库运行正常,那么 CMT 会提交,记录将出现在列表中。图 5 显示了该操作在测试应用程序中的结果。然而,有一种方法使这种菜单项产生异常。简单地运行两次会产生错误,因为相同的星星名称(作为表的主键)使用了两次,则会由于重复的主键值导致错误。如果您使用第四个菜单选项删除星星 Sol 后,试着再次创建此记录,那么由于发生这种异常,Sol 没有重新出现。在那种情况下,事务进行了回滚。


清单 4. test2() 代码,向数据库中添加星星记录
        
/**
 * Perform the second test. This test adds the stars, 
 * and if nothing goes wrong, they will be in the database.
 * @throws NamingException 
 * @throws CreateException 
 * 
 * @ejb.interface-method view-type="both"
 * @ejb.transaction      type="Required"
 */
public void test2() throws NamingException, CreateException {
   StarLocalHome home = StarUtil.getLocalHome();
   home.create("Sol");
   home.create("Betelguese");
   home.create("Andromeda");
   home.create("Aries");
   home.create("Omicron Cetus");
}


图 5. 示例程序显示的结果
示例程序显示的结果

直接删除 Sol

Transaction Demo 应用程序的 Directly remove Sol 菜单项提供了一个例子,说明如何从 Web 应用程序角度调用事务代码。这里需要 Web 层中的一些代码来直接访问星星实体并真正删除它们之中的 Sol。清单 5 中的代码显示了如何通过使用 UserTransaction 来做到这一点。InitialContext 对象,是 Java 命名和目录接口 (JNDI) API 的一部分,被用于查找使用 .java:comp/UserTransaction 标准名称的 UserTransaction 实例。获得实例后,它被用于启动和提交事务,如果发生任何错误,事务将回滚。


清单 5. Web 层事务处理例子
        
/**
* Remove the star Sol from the database by performing 
* the operation directly on the Star EJB instead of going 
* through the session bean.
* @throws NamingException if the user transaction cannot be found.
* @throws SystemException if the transaction cannot be used.
* @throws NotSupportedException if the transaction cannot be used.
*/
private void removeSol() throws NamingException, 
   NotSupportedException, SystemException {
      
InitialContext ctx = new InitialContext();
UserTransaction tx = null;
      
    try {
   // Look up the transaction
   tx = (UserTransaction)ctx.lookup("java:comp/UserTransaction");
   // Begin the transaction now
   tx.begin();
         
   StarLocal sun = 
                    StarUtil.getLocalHome().findByPrimaryKey("Sol");
      sun.remove();
         
      // We've succeeded, so commit the transaction
      tx.commit();
         
   } catch (Throwable e) {
      // An error occurred, so roll back.
      tx.rollback();
      e.printStackTrace();
   } finally {
      // Dispose of the initial context.
      if (ctx != null) {
         try {
            ctx.close();
         } catch (Throwable ex) {
         }
      }
   }
}





回页首


为新事务使用无状态会话 bean

如果您浏览了 Transaction Demo 源代码,您会发现与事务相关的代码很少。这是因为 Geronimo 容器在后台处理了大部分与事务相关的操作,这是一个好的 J2EE 容器应该完成的。每个用部署描述符 (ejb-jar.xml) 中的容器事务部分标记的无状态 bean 方法,都由 Geronimo EJB 容器自动提供有一个 CMT。CMT 是简化代码的一种非常好的方法。具有容器管理事务方法的无状态会话 bean,如 清单 5清单 6 所描述的,降低了管理数据库实体的复杂性。当您正在操作一个单一类型并且不具有任何复杂关系的实体 bean 时,使用事务类型 Required 或者 RequiresNew 简单地标记方法就足够了(请参阅 清单 6 获得实用的 DTD)。


清单 6. 摘自 ejb-jar 2.0 DTD
        
"The trans-attribute element specifies how the container 
must manage the transaction boundaries when delegating a
method invocation to an enterprise bean's business method.
The value of trans-attribute must be one of the following:
   <trans-attribute>NotSupported</trans-attribute>
   <trans-attribute>Supports</trans-attribute>
   <trans-attribute>Required</trans-attribute>
   <trans-attribute>RequiresNew</trans-attribute>
   <trans-attribute>Mandatory</trans-attribute>
   <trans-attribute>Never</trans-attribute>
Used in: container-transaction"

Transaction Demo 应用程序使用 XDoclet 代码生成器设置事务类型。所以如果检查 Listing 4 中的方法旁边的文档注释,您会发现 @ejb-transaction type="Required" 标记。Required 事务类型指明会话 bean 方法在代码执行时需要存在一个事务。如果不存在,则容器提供一个事务。如果在调用线程中已经存在一个事务,则使用该事务。

RequiresNew 事务类型不同。如果在调用线程中不存在事务,则由容器创建一个事务,与 Required 事务类型相似。然而,如果调用线程已经存在一个事务,则将此事务挂起,并创建和使用一个新事务。然后再恢复并继续运行调用事务。





回页首


结束语

通过一个设计运行于 Geronimo 应用服务器上的 Transaction Demo 应用程序,您现在应该对于如何在 J2EE 应用服务器上使用事务有一个较好的了解了。已经给出关于如何使用无状态会话 beans 设置 CMT 的例子,并且向您展示了如何通过获得 UserTransaction 类的实例创建一个事务并管理它的生命周期。现在您可以使用 Geronimo 事务来维护数据完整性了。






回页首


下载

描述名字大小下载方法
Transaction demo applicationtransactions.zip34 KBHTTP
关于下载方法的信息


参考资料

学习

获得产品和技术

讨论


关于作者

作者照片

Neal Sanche 是一位最近才涉足 Microsoft® .NET 世界的 Java 开发人员,他正在克服困难脱离他原来习惯了的开发环境。他的经验包括开发多个商业 J2EE 应用程序和多个独立 Java 应用程序。在业余时间,他作曲、摄影并撰写技术文章。访问他的 Web 站点 可以看到相应的多个示例。您可以通过 neal@nsdev.org 与 Neal 联系。




对本文的评价

太差! (1)
需提高 (2)
一般;尚可 (3)
好文章 (4)
真棒!(5)

将您的建议发给我们或者通过参加讨论与其他人分享您的想法.







回页首


IBM 是 IBM 公司在美国和/或其他国家的注册商标。 Java 和 所有基于 Java 的商标都是 Sun 公司在美国和/或其他国家的商标。 Microsoft 是微软公司在美国和/或其他国家的注册商标。 其他公司、产品或服务的名称可能是其他公司的商标或服务标志。

IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款