用EJB开发订阅信息系统

  作者:板桥banq

<<Java实用系统开发指南>>

本章通过订阅信息系统的设计和开发,介绍EJB在实践项目中的应用。当一个J2EE项目达到一定的规模,Web层和EJB层中很多类要进行频繁地交互操作。如果设计一种Web层与EJB层通用接口框架,在配置文件中设定Web层和EJB层中执行类的对应关系。这样就能完成两个大层次之间松散而且快速的联系,实现J2EE项目可动态扩展。在本章也将介绍这种接口框架系统的设计和使用。

5.1  需求分析

ejb
图5-1  用例图

在订阅信息系统中,需要记录客户订阅媒体的类型以及客户的地址,订阅媒体可以是杂志或报纸。同时要求提供查询搜索功能,能根据客户名称查询到他的订阅情况和他的住址,这是媒体发行公司的一个数据库系统。该系统的特点是:业务逻辑简单,但是数据量大,因此要求能多台服务器支持并行查询和操作。
该项目的用例图如图5-1所示。
本项目要求能新增客户资料,编辑客户订阅类型以及客户地址等资料,同时能够查询某一个具体客户的订阅情况,也可以根据媒体(杂志或报纸)的名称查询客户的订阅情况。

5.2  架构设计

EJB作为一种成熟的分布式对象组件技术,已经得到越来越广泛的应用,EJB已经成为了一种事实上的工业标准。它允许在一个供应商的EJB服务器上开发和部署的Bean能轻易地部署到其他供应商的EJB服务器上。最重要的是:开发EJB时已经不用考虑有关负载平衡、事务安全性等复杂的跨服务器技术,这些技术已经由EJB容器服务器实现,只要将具体的EJB Bean部署到EJB服务器中,经过适当简单的配置,开发者自己的应用系统就可以成为一个分布式的、集群的网络系统,这样使得开发大访问量的应用系统项目不再成为少数大型公司的专利。

5.2.1  Cache和Pool

 

  在J2EE项目开发中,包括本项目,都会将重要的业务逻辑运算封装在EJB层,Web层只是作为EJB的一种客户端。这种设计安排已经不同于之前在JSP/Javabeans下的思路,在以前JSP/Javabeans框架下,业务逻辑运算等重要功能都是在Javabeans实现。但是随着应用系统访问用户的不断增长,系统的性能需要不断提高,提高性能方式有很多,主要思路是使用Cache(缓冲)和Pool(池)机制。
在实际项目中,对象的创建(如new ClassA)或者销毁可能是非常耗时的,因为也许在涉及到数据库查询或其他网络连接,或者涉及其他大量关联对象的创建和销毁,因此事先将对象创建好,放置在内存中,这样需要时就可以直接使用。
例如使用HashMap或HashTable来保存对象引用,这样这些对象就不会被JVM的垃圾回收机制处理销毁,也就实现了实例一直保存在内存中的目的。
Pool就是在系统开始之前,预先生成一批对象实例保存在内存中,如果这些实例并行访问用户数超过实例的个数,系统将会不断再产生新的实例,直至达到Pool的最大值。如果这些实例的并行访问用户数开始降低,甚至结束,系统将在Pool中实例数减少到Pool的最低数,通过这种机制,可以实现对象的大量并行访问,从而有效地提高了系统的性能。
Cache类似Pool,所不同的是Cache中的对象是有生命周期的,Cache是将经常频繁使用的对象放在内存中,因为Cache容量是有限的,Cache中的对象如果不再被访问,将会从Cache中被清除。Cache这种机制适合那些保存有状态属性的对象。
例如有类ClassA 如下:
public class  ClassA {
   private String name = null;
   public String getName(){
      return name;
   }
   public void setName(String name){
      this.name = name;
   }
}


在类ClassA中,有一个类属性name,在客户端一次请求中,通过setName方法设置name为某个值,为了在下次客户端请求中使用保存在这个name中的值,因此需要保存ClassA实例在内存中, ClassA实例也被称为是有状态的类,因为这个类中有需要暂时保存的数据,当然还不必持久化到数据库中。
通常在Web中的做法是将ClassA实例保存到该用户的Session中,或者保存到一个Singleton类的HashTable中,并设置好特定的关键字key,这样下次依据这个关键字key读取到有该状态类的实例。
上述这些Cache和Pool都必须依靠开发者自己编码来实现。
如果应用系统要求比较高,需要事务处理机制,也就是说,在第一步实现了A数据库插入,第二步实现B数据的插入时发生问题,这个过程必须中断。如果有事务跟踪支持,那么第一步的A数据库插入动作将被取消,这样保证了两个数据库的一致性。这种事务机制也需要开发者在Javabeans中写入详细代码,为了防止死锁,还需要小心设计和处理这些事务跟踪代码,这些都给开发者带来一定的难度。
另外,为了支持分布式环境,可能还需要RMI加入一些远程对象调用功能,这些都无疑增加了开发工作量。

5.2.2  EJB框架体系

如果使用EJB,那么上述那些Cache或Pool、事务机制、安全访问权限以及分布式运算等都无需再考虑了,所有这些都将由EJB容器来完成。
在EJB中有两种类型:无状态Session Bean(Stateless session bean)和有状态Session Bean(Stateful session bean)。EJB容器使用Cache或Pool机制来支持EJB运行。
有状态Session Bean实际是运行在EJB容器的Cache中;而无状态Session Bean则是运行在EJB容器的Pool中。EJB服务器在启动时,会像Pool那样,将无状态Session Bean的实例预先生成保存在内存中,这样可以达到性能优化的目的,如图5-2所示。
ejb
图5-2  无状态Bean与Pool
当客户端访问无状态EJB时,EJB容器从实现生成的实例池中任意取出一个供该客户端访问。如果此时第二个客户端并发同时访问无状态EJB,EJB容器将取出第二个EJB实例,依次类推,如果并发访问的客户端数超过实例池中的EJB实例个数,那么EJB容器将“紧急”立即开始新产生EJB实例,直至达到实例池的最大数。
当客户端访问完毕,EJB容器将这些EJB实例返回实例池中,以供下次再访问,同时将实例池中的EJB个数维持到实例池的最小数。
在实例池中,这些无状态EJB是彼此都一样的,在容器看来没有区别,所以无状态EJB适合那些“处理机”式的服务,只要增加“处理机”个数就能提高处理性能。
相反,有状态Session Bean的每个Bean都可能不一样,因为它里面包含一些数据状态。前面例子中ClassA实现的应用同样可以使用有状态Session Bean来实现,此时就不需要开发者自己实现Cache机制了,如图5-3所示。
ejb
图5-3  有状态Bean与Cache
当客户端首次访问有状态Bean时,容器为之创建一个新的实例。该客户端在结束访问以后,这个Bean在容器内存中还是存在。这样,客户端反复访问这个有状态Bean就能获取到前几次访问保存的数据。因为这个Bean为客户端保存了某些状态。
如果该客户端不再访问该有状态Bean,容器会根据情况和设置将该有状态Bean钝化保存到持久层介质上(这是很浪费性能的事情),如果超过一定时间还没有访问,容器会自动删除这个Bean,因此,如果客户端决定不再访问该有状态Bean时,主动使用remove方法销毁这个Bean将对容器运行性能有很大帮助。
EJB的Session Bean主要是用来实现一些Service等业务逻辑运算,而EJB的Entity Bean(实体Bean)则可以认为是数据表在内存中映射,通过这种缓冲机制,系统反复访问某些数据时,就不必每次都进行数据库操作。
实体Bean又分为两种实现方式——BMP和CMP。
BMP英文是Bean Manage Persistence,表示是由开发者自己完成数据库的操作,即类似在Web层中访问数据库的Javabeans写法,在这些Javabeans中要通过写入SQL语句实现数据库数据的新增、修改或删除。因为开发者可以自己编写很复杂的SQL语句,因此BMP的定制功能非常强大,缺点是不支持集群环境。
CMP的英文是Container-Managed Persistence,表示是由容器安排管理的,与BMP相反,在CMP中,很少需要编写SQL语句。CMP的好处是因为容器代理数据库操作,因此,实现集群Cluster就很容易,非常方便的构架一个分布式计算环境。
CMP从EJB2.0以后得到强有力的支持,引入EJB-QL,类似一种SQL的语句,是对CMP容器不能实现的功能补充,还有CMR(Container-Managed Relationships),用来实现数据表之间的一对多、多对一或多对多的关系。
CMP还有一个好处:结合强大的CMP开发工具如JBuilder,就可以实现快速的可视化开发。
本项目的数据模型将采取EJB的CMP实现,结合JBuilder强大工具的可视化开发,可以达到迅速开发的目的。
当然,持久层的技术还在不断发展,O/R Mapping技术已经日趋成熟。因为在应用系统中经常都是使用数据对象,要将数据对象保存到关系数据库中。这其中需要开发者自己来实现这种转换,而采取实现O/R Mapping技术的一些工具,如hibernate,就可以直接将数据对象持久化保存到数据库,这无疑更加提高了开发效率,O/R Mapping取代实体Bean将是一个趋势。

下页