J2EE在线的银行应用程序我们讨论DUKE的银行应用程序,一个在线的银行应用程序.他有两个客户端,一个让管理员管理顾客和账号的j2ee应用程序客户端,一个让顾客访问账号历史和执行的交易信息的web客户端。顾客通过实体bean访问存储在数据库中的顾客,账号,和交易信息。DUKE银行应用程序向我们展示了我们在这本书中介绍的所有的组件-EJB,j2ee应用程序客户端和web组件是如何在一起协同工作以组成一个简单但又功能丰富的应用程序的。下面的图片是一个在高层次上的组件交互图。在这一章我们将详细讨论他们的类型,包括他们是如何编译,部署,和运行的。图17-1duke银行应用程序EJB下图展示了客户端,EJB和数据库表之间的访问路径。正如下图所示,客户端应用程序仅仅只访问会话BEAN,在EJB之间的关系中,会话BEAN是实体BEAN的客户端。在应用程序的末端,实体BEAN通过访问数据库中的表存储实体的状态。这些EJB的原代码位于j2eetutorial/bank/src/com/sun/ebank/ejb子目录。图:17-2duke银行应用程序中的EJB会话BEANDUKE的应用程序有三个会话BEAN,AccountControllerEJB,CustomerControllerEJB和TxControllerEJB(Tx代表一个业务交易,比如银行转账)这些会话BEAN向客户端提供了一个应用程序业务逻辑的视图。它们隐藏了服务器端执行业务逻辑,访问数据库,管理关系和检查错误的细节。AccountControllerEJBAccountControllerEJB的业务方法根据执行的任务可以分为几类:生成和删除实体BEAN,管理顾客和账号之间的关系,获得账号的信息。下面的两个方法生成和删除实体BEAN。createAccountremoveAccountAccountControllerEJB会话BEAN的这两个方法调用AccountEJB实体BEAN的create和remove方法。如果参数错误,createAccount和removeAccount方法将抛出应用程序级的异常。如果参数的类型不是Checking,Savings,Credit和MoneyMarket,createAccount方法将抛出IllegalAccountTypeException异常。createAccount方法也通过调用CustomerEJB实体BEAN的方法findByPrimaryKey来确定特定的顾客是否存在,如果顾客不存在,createAccount方法抛出CustomerNotFoundException异常。下面的方法管理账号和顾客之间的关系。addCustomerToAccountremoveCustomerFromAccountAccountEJB和CustomerEJB实体BEAN之间有着多对多的关系。一个账号可以被多个顾客使用,一个顾客也可以有多个账号。因为实体BEAN使用BMP(Bean管理持久性关系),所以有多种方法处理这种关系。在Duke的银行应用程序中,AccountControllerEJB会话BEAN的使用addCustomerToAccount和removeCustomerFromAccount方法管理账号和客户之间的关系。例如addCustomerToAccount方法开始先确定一个顾客是否存在。为了实现这种多对多的关系,addCustomerToAccount方法向数据库表customer_account_xref插入一行,在这个交叉引用的表中,每一行都包括相关实体的customerId和accountId字段。为了删除这种关系,removeCustomerFromAccount方法从customer_account_xref表中删除一行。下面的方法得到有关账号的信息。getAccountsOfCustomergetDetailsAccountControllerEJB会话BEAN有两个get方法,getAccountsOfCustomer方法通过调用AccountEJB实体BEAN的findByCustomer方法返回一个给定顾客的所有账号,为了取代对AccountEJB的每一个变量(即与数据库表相对应的字段)都执行get方法,AccountControllerEJB会话BEAN通过一个getDetails方法返回一个封装了AccountEJB实体BEAN状态的对象(AccountDetails对象)。CustomerControllerEJB因为AccountControllerEJB会话BEAN管理顾客和账户之间的关系,所以CustomerControllerEJB会话BEAN相对简单一些。客户端通过调用CustomerControllerEJB会话BEAN的方法createCustomer创建一个顾客,通过调用removeCustomer删除一个顾客,它不仅调用CustomerEJB实体BEAN的remove方法,还删除customer_account_xref表中包含相应顾客的所有行。CustomerControllerEJB会话BEAN中有两个方法返回多个顾客,getCustomersOfAccount和getCustomersOfLastName,这两个方法调用CustomerEJB实体BEAN的相应的finder方法findByAccountId和findByLastName。TxControllerEJBTxControllerEJB会话BEAN处理银行交易。除了他的get方法getTxsOfAccount和getDetails,他还有几个方法用于改变一个账号中的余额。withdrawdepositmakeChargemakePaymenttransferFunds这些方法通过访问AccountEJB实体BEAN来确定账号的类型和设置账号中的余额。withdraw和deposit用于非信用卡的账号。makeCharge和makePayment用于信用卡账号。如果账号的类型不符合,这些方法抛出IllegalAccountTypeException异常。如果在取款后,账号中的余额为负数,withdraw则抛出InsufficientFundsException异常。在用信用卡支付中,如果超过了信用卡中的上限,makeCharge方法抛出InsufficientCreditException异常。transferFunds方法不仅检查账号的类型还检查账号中的余额。如果需要,它抛出和withdraw,makeCharge方法相同的异常。transferFunds必须检查一个账号上的余额,并把它加到另一个账号上,这两步必须完成,因此transferFunds需要事务支持,如果其中的一步失败了,事务回滚,余额保持不变。实体BEAN在我们简单的小银行中,每一个业务实体在duke银行应用程序中都有一个对应的实体BEANAccountEJBCustomerEJBTxEJB这些实体BEAN的目的是为了提供account,customer,tx这几个数据库表的对象视图,对数据库表中的每一行,都有一个实体BEAN的实例变量与之对应。因为这些实体BEAN使用BMP,所以他们包含访问这些数据库表的SQL语句。例如CustomerEJB实体BEAN的create方法调用SQL语句的INSERT命令。不像会话BEAN,这些实体BEAN的方法不验证参数,除了ejbCreate方法的主键参数。在设计阶段,我们决定在会话BEAN中验证参数,并抛出应用程序级的异常,例如CustomerNotInAccountException和IllegalAccountTypeException异常。因此,假如其他的应用程序使用这些实体BEAN,它的会话BEAN仍然必须验证方法的参数。帮助类在EJB的jar文件中包含了几个被EJB使用的帮助类,这些帮助类的源代码位于j2eetutorial/bank/src/com/sun/ebank/util目录下。下面的表格简单的表述了这些帮助类。类名描述AccountDetails封装了AccountEJB的实例状态,被AccountEJB和AccountControllerEJB的getDetails方法返回。CodedNames定义了在调用lookup方法中使用的字符串的逻辑名称,例如java:comp/env/ejb/account,EJBGetter类引用这些字符串。CustomerDetails封装了CustomerEJB的实例状态,被CustomerEJB和CustomerControllerEJB的getDetails方法返回DBHelper提供一些产生下一个主键的方法。例如getNextAccountId方法Debug提供一些简单的方法打印EJB的编译信息。如果j2eeserver使用-verbose选项运行,这些信息出现在server的控制台上DomainUtil包含一些验证方法,例如getAccountTypes,checkAccountType,isCreditAccount。EJBGetter包含一些方法(通过调用lookup方法)定位并返回HOME接口。例如getAccountControllerHomeTxDetails封装了TxEJB的实例状态,被TxEJB和TxControllerEJB的getDetails方法返回表17-1duke应用程序EJB的帮助类数据库表在duke的银行应用程序中,数据库的表可根据他们的目的分类,一类代表业务实体,一类管理产生下一个主键。代表业务实体的表下图展示了数据库表之间的关系。customer和account表之间有一个多对多的关系。一个顾客可能有多个账号,一个账号也可能被多个顾客所拥有。这个多对多的关系通过交叉表customer_account_xref来实现。account和tx表有一个一对多的关系。在一个账号上可以进行多次业务交易,但是一次业务交易只能引用一个账号。图:17-3duke应用程序中的数据库表在图中我们使用了几个简写。PK代表主键(primarykey)它的值唯一确定了数据库表中的一行。FK是外键的简写,这个字段是被引用的数据库表中的主键。Tx代表一个业务过程。例如取款和存款。管理下一个主键的表这些表有下面几个:next_account_idnext_customer_idnext_account_idnext_tx_id这些表的每一个中都有一个单独的列叫做id,他的值被传给实体BEAN的create方法。例如,在创建一个AccountEJB实体BEAN之前,AccountControllerEJB会话BEAN必须通过调用DBHelper类的getNextAccountId方法获得一个唯一的值。getNextAccountId从next_account_id表中读出id的值,并在数据库表中增加id的值,返回id。保护EJB在j2ee平台,你可以建立访问EJB方法的角色,相应的角色访问EJB相应的方法。在duke的银行应用程序中,根据他们的操作类型定义了两种角色,银行顾客和银行管理员。属于银行管理员角色的用户,可以执行管理功能:创建和删除一个账户,给一个账户增加或者删除顾客,设置信用卡的上限,设置初始账号的余额。属于银行顾客角色的用户,可以存款取款,转账等功能。注意:两个角色可执行的功能上不会有重叠。通过在CustomerControllerEJb,AccountControllerEJB和TxControllerEJB会话BEAN的特定的方法上设置访问允许权,限制角色对这些方法的访问。例如,可以允许只有属于银行管理员角色的用户可以访问AccountControllerEJB的createAccount方法,可以拒绝属于银行顾客角色或其他角色的用户创建账号。为了查看是否设置了方法的允许权,在de