应用程序设计:
数据层:
在数据层,我们需要两个不同的数据库,在每个数据库中都存在一张表。为了让例子更加真实,我们会使用两个不同的数据库,因为从不属于用户账户所在银行的ATM上取钱是确实可能出现的情况。(根据以下信息配置数据库)
banktest包括account表,表示用户账户信息。
atmtest包括atm表,表示ATM信息。
逻辑层:
在逻辑层,我们有三个类访问资源并在资源上进行各种操作:
foo.BankAccount表示指定用户的银行账户并通过JDBC对数据库中的account表进行操作。
bar.ATM表示ATM并通过JDBC对atm表进行操作。
bar.CashDelivery使用上面两个类完成用户取钱的操作。
所有的逻辑都在CashDelivery.java类的deliverCash方法中完成。
javax.transaction.UserTransaction接口被用来指定是否使用事务,所有在utx.begin()和utx.commit()(或者utx.rollback())之间的操作都会在一个事务中完成。这就保证了你的Web应用不会陷入上面的场景所讨论的痛苦之中。
感谢事务,让应用的逻辑变得如此简单,仅仅包括如下几步:
1)开始事务;
2)与用户的银行联系,并从账户上支取相应的数额;
3)告诉ATM提供现金;
4)结束事务
a) 如果所有的事都成功完成,那么提交事务;
b) 否则,回滚事务
5)将事务的结果向用户报告。如果事务成功完成,那么用户得到现金,并且账户上会扣除相应的数额。否则,什么事都不会发生。
示例一:
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;
}
}
}