简介
j2ee 除了提供了 servlet 之外,还提供了大量的其它功能。servlet 开发者们也许难得使用这些功能,不情愿也没有时间用一个超出所需的大型 j2ee 服务器来替换自己的简单的 servlet。然而,依据j2ee 的模块化特征,有可能将负责特定 j2ee 功能的小组件整合到 servlet 容器里,以此来增强 web 应用程序。其中之一就是事务。
有关 j2ee 事务的完整描述,您可以参考three onjava articles。现在只需知道事务是资源的操作步骤(例如:数据库),它由四个属性定义,这四个属性根据其首字母浓缩为 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. 完成事务。
o 如果所有事件完成,提交事务。
o 否则,回滚事务。
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,,用于显示和更新两个资源的信息。(它不属于应用程序设计的部分,但是添加它来简化资源更新,比如处理客户帐户的钱数。)
配置数据库
关于数据库,建议使用 mysql 4.0.12 和相应的 jdbc 驱动程序。默认情况下,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 应用程序中
闽公网安备 35060202000074号