newhappy的专栏

高级系统分析师,专注于对SOA,Ajax以及J2EE架构的研究,欢迎大家与我交流.Email:pleasechess@126.com

用户操作
[即时聊天] [发私信] [加为好友]
newhappyID:newhappy2008
491486次访问,排名93好友62人,关注者73
吉林大学软件工程硕士;一汽启明CPDM项目组软件工程师;
newhappy2008的文章
原创 187 篇
翻译 24 篇
转载 99 篇
评论 297 篇
newhappy的公告
非常感谢CSDN提供了这么好的一个平台,过去的一年为生活而忙忙碌碌,博客更新的不多,在新的一年里,我会勤快一点,多学一些技术,多交一些技术上的朋友.
最近评论
hanming808:很好
meng0819:这些问题在设计阶段,除非是神,任何人在当时当地都无法完全预料到。如果在早期阶段就投入精力到性能中,只会得不偿失。

评:设计完成后的评审可以避免类似的问题。
bingg:呵呵,任何事情都是发展的,变化的,楼主喜欢给事物下定义是不对的.鄙视一下
bingg:呵呵,任何事情都是发展的,变化的,楼主喜欢给事物下定义是不对的.鄙视一下
bjthemost:没什么没有人关心 Berkeley DB?
文章分类
收藏
    相册
    友情连接
    114社区
    SOA-中间件
    张孝祥(RSS)
    杨洪波(RSS)
    沈东良
    许式伟(RSS)
    谭振林(RSS)
    银狐999(RSS)
    阿蒙专栏(RSS)
    存档
    订阅我的博客
    XML聚合  FeedSky

    转载 用 JOTM 向Servlet中添加事务收藏

    新一篇: SEO标准模板 | 旧一篇: 软件设计是决定软件性能的关键

    J2EE 除了提供了 servlet 之外,还提供了大量的其它功能。Servlet 开发者们也许难得使用这些功能,不情愿也没有时间用一个超出所需的大型 J2EE 服务器来替换自己的简单的 servlet。然而,依据J2EE 的模块化特征,有可能将负责特定 J2EE 功能的小组件整合到 servlet 容器里,以此来增强 WEB 应用程序。其中之一就是事务。有关 J2EE 事务的完整描述,您可以参考Onjava上的其他三篇文章,现在只需知道事务是资源的操作步骤(例如:数据库),它由四个属性定义,这四个属性根据其首字母浓缩为 ACID:

      原子性:事务的操作,或者是全部成功(此时提交事务),或者是全部不成功(此时回滚事务),谓之为 all-or-nothing 属性。一个事务应该被视为单个工作单元,在一个事务里面绝对不可能同时存在完成了的和没有完成的操作。

      一致性:完成了的事务将资源从一个有效状态转变为另一个有效状态。一致性的具体例子有:数据库的参照完整性和表中的主键唯一性。

      独立性在事务没有提交之前,事务作用的共享资源的改变在事务之外是不可见的。独立性确保了不同事务不会同时访问正在更新的数据。

      持久性:由事务提交的改变会永久存在。

      JOTM (Java Open Transaction Manager)是由ObjectWeb协会开发的功能完整的且资源开放的独立的事务管理器。它提供了 JAVA 应用程序的事务支持,而且与 JTA( JAVA 事务 API)兼容。您可以在JOTM home page了解到更多的详细信息。在 TOMCAT(或其它 Servlet 容器)整合了 JOTM 后,JSP 和 servlet 的开发者们就可以获得事务的优势轻而易举的创建更多健壮的 web 应用程序。

      为了突出事务是怎样增强 web 应用程序的,举一个常用的例子, web 浏览器与客户端交互的 ATM 。

      ATM 样例:

      情景

      此例比较简单:一个客户想从 ATM 提款,输入了他的客户名称,john_doe;想提款数,$50。如果他的银行帐户上有足够的钱并且在 ATM 机上有足够的现金的话,应用程序就能给他相当数目的现金,并从银行帐户上提出同样的数目。否则,操作中断,并且除出现错误信息之外,其他都不会改变。我们无需担心安全问题,只是在猜想用户是否正确授权。

      这是一个非常简单的例子,但是如果不使用事务,用别的方法执行起来将会很难。客户端操作将会涉及到两个不同的资源:ATM 和客户银行帐号。它们会自动的在应用程序设计中产生 ACID 问题。例如:如果在 ATM 上操作成功而在银行帐户上却失败(也许是因为交流失败),客户将会取到钱,但是他的帐户将不会更新。对于银行来说,这就亏大了。更糟的是,如果银行帐户更新了,但是由于一个错误阻止 ATM 传送钱,客户得不到现金,但是帐户上却提掉了这笔款。

      为了防止出现上述事故,在你的应用程序里,你能够 1) 联系两个资源,并告知两者客户执行的所有当前操作,2) 询问两者是否能执行操作,3)如果两者都同意,则请求操作。即使这样,此方法也不能谓之足够健壮,因为,如果客户帐户上的钱在第二步和第三步的时候被另外一操作提走,提款可能会失败,例如,客户帐户不能出现逆差。

      事务能使应用程序更简单更健壮的之处就是:在同一事务的两个资源上执行所有的操作的时候,它将会解决 ACID 的问题(尤其是原子性)。

      应用程序设计

      数据层:在数据层,有两个不同的数据库,并各自有一张表。为了使例子更接近实际,我们使用两个不同的数据库,因为有可能从 ATM 提走不是属于该客户帐户的款(请参见下文配置数据库)。

      banktest 包含代表客户帐号的 account 表。

      atmtest包含代表 ATM 的 atm 表。

      逻辑层:在逻辑层,有三个类来访问资源和执行操作:

      foo.BankAccount 代表给定客户的银行帐号 account,并能通过 JDBC在 account 执行数据库操作。

      bar.ATM 代表 ATM,并在 atm 表上执行 JDBC 操作。

      bar.CashDelivery 使用前面两个类来执行一个客户操作。

      所有逻辑在 CashDelivery.java 的 deliverCash 方法中实现。

      javax.transaction.UserTransaction 接口用于划分事务所有 utx.begin() 和 utx.commit() (或 utx.rollback())之间的操作在同一事务内执行。这确保了应用程序不会受到如前述的遭遇。

      事务使得应用程序更为简单,由以下简单的步骤组成:

      1. 开始事务。

      2. 联系客户的银行帐户并从帐户上提款。

      3. 告诉 ATM 传送钱。

      4. 完成事务:如果所有事件完成,提交事务。否则,回滚事务。

      5. 报告客户事务结果。如果事务成功,现金将被提出,钱数也将从帐户上提出。否则,一切都不会改变。

    例1. CashDelivery.java

    public boolean deliver(String client, int value) {
    InitialContext ctx          = new InitialContext();
    UserTransaction utx = (UserTransaction)
    ctx.lookup("java:comp/UserTransaction");
        ...
    boolean success = false;
    try {
    // 开始事务
    utx.begin();
    
    //联系客户银行帐户...
    BankAccount account = new BankAccount(client);
    
    // ... 从帐户上提款
    account.withdraw(value);
    
    //联系 ATM...
    ATM atm = new ATM();
    
    // ... 传送现金给客户
    atm.deliverCash(value);
    
    //一切正常
    success = true;
    
    } catch (Exception e) {
    // 出现故障,不得不
    // 报告给客户
    explanation += e.getMessage();
    
    } finally {
    try {
    if (success) {
    /*一切正常提交事务
    直到现在,钱才真正的从帐户上提出,并且将现金传送给客户。
                    */
    utx.commit();
    
    } else {
    /* 出现故障,就回滚事务。
    *所有在事务内处理的操作不会发生。
                    */
    utx.rollback();
                }
    } catch (Exception e) {
    /* 在完成事务的过程中出现故障,
    *仍旧保证
    * 事务内的操作不会发生。/
                */
    // 报告给客户
    explanation += "\n" + e.getMessage();
    
    //最后,事务不会成功
    success = false;
    
    } finally {
    return success;
            }
        }
    }

      表示层:在表示层,就用程序由两个 JSP 文件组成:

      atm.jsp, 应用程序,它发送给bar.CashDelivery 类客户登录和提款数目,并显示客户操作的结果 。

      admin.jsp,,用于显示和更新两个资源的信息。(它不属于应用程序设计的部分,但是添加它来简化资源更新,比如处理客户帐户的钱数。)

     

    图1 应用程序设计

      配置数据库

      关于数据库,建议使用MySQL 4.0.12和相应的 JDBC 驱动程序(见Resources)。默认情况下,MySQL 表不会受影响。为支持事务,表在创建的时候设置为 InnoDB 类型。另外,为启用 InnoDB 类型,您可以将 MySQL 配置文件内的 #skip-innodb 行注释掉。

      已配置了一个 MySQL 的例子,用户名为 javauser,密码为 javadude。确保该用户已被创建并且拥有创建数据库的权限。

      创建数据库和表的脚本在 scripts/ 目录下的 example file 内含有。它将创建一个 account 表并插入两个客户:john_doe 他的帐户金额为 $100。jane_doe 他的帐户金额为 $600。

    例2 创建 account 表

    mysql> CREATE DATABASE banktest;
    mysql> USE banktest;
    mysql> CREATE TABLE account(
    -> client VARCHAR(25) NOT NULL PRIMARY KEY,
    -> money INT) TYPE=InnoDB;
    mysql> INSERT INTO account valueS("john_doe", 100);
    mysql> INSERT INTO account valueS("jane_doe", 600);
    mysql> SELECT * FROM account;
    +----------+-------+
    | client   | money |
    +----------+-------+
    | john_doe |   100 |
    | jane_doe |   600 |
    +----------+-------+

    脚本还会创建有 $500 可用现金的 atm 表。

    例3 创建 atm 表

    mysql> CREATE DATABASE atmtest;
    mysql> USE atmtest;
    mysql> CREATE TABLE atm(
    -> id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    -> cash INT) TYPE=InnoDB;
    mysql> INSERT INTO atm valueS(null, 500);
    mysql> SELECT * FROM atm;
    +----+------+
    | id | cash |
    +----+------+
    |  1 |  500 |
    +----+------+

      最后,复制 $CATALINA_HOME/shared/lib 内的 JDBC 驱动程序 .jar 文件。

      获取并安装 TOMCAT:本章主要介绍 Tomcat 4.1.18 及以上的版本。首先确保没有使用以前的旧版本,安装 TOMCAT 没有什么特别,只需下载并解压缩即可。

      获取并安装 JOTM:如果要使用 JOTM,只需要下载最近的二元版本并将解压缩即可。再从lib/ 目录下将.jar 文件(除了 log4j.jar、ommons-cli.jar 和 jotm_iiop_stubs.jar) 复制到 $CATALINA_HOME/shared/lib。这样就完成了。

      配置 TOMCAT:需要配置 Tomcat,使之能够从 JNDI 获取 UserTransaction 和 DataSource 对象(它们用在 foo.BankAccount 和 bar.ATM)。

      首先,告诉 TOMCAT 你所使用的 JNDI 名字,以便在 WEB 应用程序中查询数据源。这些步骤由 web.xml 完成,其代码如下。对于银行帐户数据源,使用的 JNDI 名字是 java:comp/env/jdbc/bankAccount ,而且只能在 java:comp/env/ 之后给出名字。TOMCAT 通过 JNDI 机制来解决其余的问题。对于 ATM 数据源也同样于此。

    例 4. web.xml

    <web-app>
    <resource-env-ref>
    <description>Bank Account DataSource</description>
    <resource-env-ref-name>jdbc/bankAccount</resource-env-ref-name>
    <resource-env-ref-type>javax.sql.DataSource</resource-env-ref-type>
    </resource-env-ref>
    
    <resource-env-ref>
    <description>ATM DataSource</description>
    <resource-env-ref-name>jdbc/ATM</resource-env-ref-name>
    <resource-env-ref-type>javax.sql.DataSource</resource-env-ref-type>
    </resource-env-ref>
    </web-app>

      您必须告诉 TOMCAT 怎么样返回 web.xml内的资源,这个过程就由bank.xml文件来完成了。对于银行帐户和 ATM 资源,您必须设置参数,以便 TOMCAT 能将 WEB 应用程序与数据源正确相连。有关更多的详细信息,请参考 TOMCAT JNDI 数据源基础知识。(参见 Resources)

      其中一个参数需特别关注:factory.类中设置这个参数,用于当 WEB 应用程序通过 JNDI 查询时来创建一个数据源。另外一个重要的资源(在 web.xml 中有描述)是 UserTransaction。java:comp/UserTransaction 使用这个资源来区分事务,它由 JOTM 来执行。

    例 5 bank.xml

    <Context path="/bank" docBase="bank.war" debug="0" 
    reloadable="true" crossContext="true">
    
    <!-- Description of the DataSource "jdbc/bankAccount" -->
    <Resource name="jdbc/bankAccount" auth="Container" 
     type="javax.sql.DataSource" />
    <ResourceParams name="jdbc/bankAccount">
    <parameter>
    <!-- Factory of the DataSource -->
    <name>factory</name>
    <value>org.objectweb.jndi.DataSourceFactory</value>
    </parameter>
    <parameter>
    <name>url</name>
    <value>jdbc:mysql://localhost/banktest</value>
    </parameter>
    <!-- other parameters include:
    o username - name of database user
    o password - password of the database user
    o driverClassName - JDBC Driver name
            -->
            ...
    </ResourceParams>
    
    <!-- Description of the DataSource "jdbc/ATM" -->
    <Resource name="jdbc/ATM" auth="Container" 
     type="javax.sql.DataSource" />
    
    <!-- same type of parameters than for resource 
     "jdbc/bankAccount" -->
    <ResourceParams name="jdbc/ATM">
            ...
    </ResourceParams>
    
    <!-- Description of the resource "UserTransaction -->
    <Resource name="UserTransaction" auth="Container" 
     type="javax.transaction.UserTransaction" />
    <ResourceParams name="UserTransaction">
    <parameter>
    <name>factory</name>
    <value>org.objectweb.jotm.UserTransactionFactory</value>
    </parameter>
    <parameter>
    <name>jotm.timeout</name>
    <value>60</value>
    </parameter>
    </ResourceParams>
    </Context>

      展开 WEB 应用程序:一旦你设置了 JOTM 和TOMCAT ,展开并使用 WEB 应用程序就很容易了。首先,下载 bank.tgz 并将之解压缩, 再将bank.xml 和 bank.war 复制到 $CATALINA_HOME/webapps 下;然后,启动 TOMCAT:

    > cd $CATALINA_HOME/bin
    > ./catalina.sh run
    Using CATALINA_BASE:/home/jmesnil/lib/tomcat
    Using CATALINA_HOME:/home/jmesnil/lib/tomcat
    Using CATALINA_TMPDIR:/home/jmesnil/lib/tomcat/temp
    Using JAVA_HOME:/usr/local/java
    May 6, 2003 5:56:00 PM org.apache.commons.modeler.Registry loadRegistry
    INFO:Loading registry information
    May 6, 2003 5:56:00 PM org.apache.commons.modeler.Registry 
     getRegistry
    INFO:Creating new Registry instance
    May 6, 2003 5:56:00 PM org.apache.commons.modeler.Registry 
     getServer
    INFO:Creating MBeanServer
    May 6, 2003 5:56:07 PM org.apache.coyote.http11.Http11Protocol init
    INFO:Initializing Coyote HTTP/1.1 on port 8080
    Starting service Tomcat-Standalone
    Apache Tomcat/4.1.24-LE-jdk14

    您会在日志里面发现 JOTM 还没有启动。它是在当您第一次访问 DataSource 时才会启动的,在那个时候,您将会发现以下信息:

    May 6, 2003 5:56:20 PM org.objectweb.jotm.Jotm <init>
    INFO:JOTM started with a local transaction factory that 
     is not bound.
    May 6, 2003 5:56:20 PM org.objectweb.jotm.Jotm <init>
    INFO:CAROL initialization

    键入URLhttp://localhost:8080/bank/来使用 WEB 应用程序。

      使用 WEB 应用程序

      WEB 应用程序的首页包含两个链接:

      1. 是 Cash Delivery 页面,您可以在上面像在 ATM 一样提款。

     

    图2 Cash Delivery 页面

      2. 是management console,您在上面可以对 ATM 或自己创建的银行帐户进行检测或更新。

     

    图3 Management Console

    操作之前,ATM 有$500,John Doe 银行帐户上有 $100 ,Jane Doe 银行帐户有 $600 。

    如果 John Doe想取 $400 ,交易将会失败,因为在他的帐户上余额不够。结果将是:

    Client ID:john_doe, value: $400

    Cash can not be delivered to you

    because:not enough money in your account (only $100).

    如果 Jane Doe想取 $550 ,交易也会失败,因为ATM上的现金不够。结果将是:

    Client ID:jane_doe, value: $550

    Cash can not be delivered to you

    because:not enough cash available from this ATM (only $500).

    如果 John Doe 取 $50 的话,交易将会成功。结果将是:

    Client ID:john_doe, value: $50

    Please take your cash ($50)

    Thank you!

      总结

      这个简单的例子证明了 servlet 是怎样通过使用事务提供健壮和简化的,并且确保在任何情况下都正确。Tomcat 和 JOTM 完美的结合使在 servlet 内能轻而易举的取得事务的优势。

      除上述简单的例子以外,JOTM还有更多的优点。JOTM 提供了以下性能,有助于增强 WEB 应用程序。

      1.完全分布式事务支持.如果数据层、业务层、表示层运行在不同的 JVM 上,则有可能有一个全程的事务跨度这些JVM,事务的内容在 RMI/JRMP 和 RMI/IIOP 上传播。

      2.整合 JDBC。使用的 XAPool例子就是一个 XA-兼容的 JDBC 连接池,可以与数据库相互操作。XAPool 类似于 Jakarta DBCP,只是增加了 XA-兼容的特征,如果要结合 JDBC 使用 JTA 事务就必须遵从这个特征。

      3.整合 JMS。JOTM 可以结合 JORAM,由ObjectWeb 协会开发的“JMS 提供者”提供了事务的 JMS 消息。你可以得到出现在 servlet中同一事务的 JMS 消息发送件和更新的数据库。

      4.WEB 服务事务。JOTM 提供了BTP(Business Transaction Protocol)、JOTM-BTP接口,它们用于在 WEB 服务中增加事务行为。

      所有这些功能的样例和文档都可以在 JOTM 的档案和网站上找到。

    发表于 @ 2008年07月05日 19:20:00|评论(loading...)|收藏

    新一篇: SEO标准模板 | 旧一篇: 软件设计是决定软件性能的关键

    评论

    #yanhaiyijian 发表于2008-07-25 18:46:24  IP: 210.83.203.*
    牛人啊!
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © newhappy