我作为BEA的顾问,已经成功地帮助客户在各种版本的WebLogic Server (WLS)上设计并部署了应用程序。BEA从EJB 1.0已经开始支持容器管理(CMP)的实体beans,并且已有一些客户在使用它们。可不幸的是,一些客户在不理解实体bean的情况下就使用上了它们;而另一些客户一听说它会对性能有某些约束,就完全从他们的结构/设计选择上排除使用实体beans。
当面对系统分析师的询问"我究竟何时应该考虑去使用CMP方式的实体beans呢?"的时候,我很难解释为什么要使用CMP形式的实体beans。实体beans的优势体现在两方面:高速缓存和对象-关系的映射。两者在先前的版本中都勉强的被实现,但EJB 2.0拯救了这种情况。WLS 7.0在EJB 2.0规范上的附加功能弥补了在使用实体beans和使用无状态会话beans之间在性能方面的差距,说明:无状态会话bean是通过DAO和JDBC访问数据库的,在某些情况下实体bean甚至比无状态会话bean执行的快。当使用JDBC时,强烈建议开发者使用容器管理的事务。
早期版本的问题
一些在过去使用实体beans的客户不得不重新设计和使用JDBC,因为他们的系统太慢以至于无法满足服务。缓慢的原因在于:
没有高速缓存: 每个事务为了加载实体bean都访问数据库。客户在使用实体bean时所期望的承诺之一是缓存,而对规则实体beans来说在集群环境中是没有缓存可以使用的。
单一表支持 (O-R 映射过分简单化): 客户也期望对象-关系映射可以由实体bean完成,但是由容器提供映射功能过分简单了。
每个虚拟机(VM)只能有该实体bean的单一实例:由于WebLogic 5.1和更低的版本只支持互斥锁,所有对实体bean的调用都是串行化的,这样会引起系统瓶颈甚至是对只读beans。
互斥锁 :当一些beans在使用事件而另一些没使用的时候,死锁发生的可能性很高。
单一setX调用:这引起整个实体bean都被写进数据库。在EJB 2.0 规范前,容器对于判断哪些内容发生了改变哪些没有发生改变是没有办法的,这意味着在ejbStore时,所有的属性都要更新。
装载 :装载实体bean,使所有的数据成员都装进内存,只需要几个的时候。
无动态查询 :写了一个新的查询时,EJB必须被重新部署。
查询 :查询会实例化大量的实体beans,消耗内存并且降低系统性能。
WLS 7.0的特征
WLS 7.0 实现了在EJB 2.0中提供的丰富的对象-关系映射规范。在优化读写数据库方面EJB 2.0也为容器提供了更丰富的便利。下面说明了开发者在WLS 7.0 中设计实体beans方面所拥有的丰富和灵活的特征。
容器管理的关系 :实体beans可以关联其他的beans;这些关系可以是双向的或是单向的。WLS支持三种类型的关系映射,由WebLogic CMP管理:一对一,一对多,多对多。
一个实体bean的多表映射:多表的支持允许一个符合EJB 2.0特征的CMP beans的实现器将一个EJB 映射为一个据库中的多DBMS表。
调优EJB2.0的写操作:当调用ejbStore时只更新写进数据库的字段,而不是把所有字段都写进数据库。
使用字段组调优读操作:在ejbLoad容器,只装载field-group中的字段,而不是装载实体bean的所有字段。
关系的缓存: 这个特征提高实体bean的性能,通过在某一个联合查询(非多重条件查询)中缓存并装载相关的beans。
实体beans可以返回ResultSets(记录集):WLS 支持ejbSelect()查询,它在java.sql.ResultSet的表单中返回多列的查询结果。
对不同种类的实体beans的应用级的缓存: 代替对每个实体bean分散的缓存,这个特征可以使多个实体beans 在一个EAR中共享一个单一运行时的缓存。
动态查询: EJB 2.0规范强制用户在部署描述符中编写查询代码。通过采用动态查询,新的查询可以通过程序被构造和执行而不用重新部署beans。
Read-mostly设计模式:在现实世界中,大部分应用程序的90% 为读取而10%为更新。实体bean可以被写成模拟数据并且可以部署为只读实体bean和规范的实体bean。通过这种方法,想要读取数据的用户与只读实体bean交互,而想要修改数据的用户与规则的实体bean交互。当修改发生时,容器使所有的只读实体bean失效,当数据访问再次发生时迫使容器调用ejbLoad。
其他有用的特征包括:自动生成主键,支持级联删除,支持EJB QL(Query Language,查询语言),ejbSelect方法,以及下面所描述的并行策略。
并行策略
并行策略定义一个实体bean创建多少实例,为了维持事务的完整性谁来实现同步以及在实体bean中的数据模型存取模式。恰当的并行策略可以使实体bean在性能上产生极大的不同。
互斥性:对于每个VM实体bean容器只产生一个实例,所有实体bean的调用都作为使用容器锁的实体bean来串行化。这绝不被推荐使用。
数据库:容器延期对数据库的锁定,并且每个事务都有它自己实体bean的拷贝。ejbLoad 和 ejbStore 在各自事务的开始和结束时被调用。
只读实体beans: 数据库和容器都不支持任何的锁,每个事务得到它自己的实体bean的拷贝。一个叫做的可配置参数用于控制实体bean调用ejbLoad方法。EjbStore在实体bean上永远不会被调用。客户端仍然可以在实体bean上进行创建,移除和更新操作。创建和移除操作会成功,而由于不调用ejbStore,更新操作将不会修改数据库。在只读实体bean上不调用CUD(create, update, delete)方法是程序员的责任。只读实体bean对于"分布式缓存"问题是最好的解决方法。
开放式的实体beans :容器延期对数据库的锁定,但是该锁在事务期间不予以保留。其基本思想是容器在提交前检查修改了的数据并且如果其他人也修改了数据时,将该操作回滚。如果你想有比TX_READ_COMMITTED更高的一致性,而不需要达到TX_ SERIALIZABLE那么高的一致性时这样做是很有用的。如果你能忍受短时间读取陈旧的数据,而又想完成一些update操作的事务完整性时,你可以使用它。这里有四个选项可用于检查optimistic式冲突:
1. 检查读取的列
2. 检查修改的列
3. 检查时间信息列
4. 检查版本信息列
选项3和4是不被推荐的,因为为适应这个并行策略,表的结构需要改变。
同样,对于开放式并行策略,你可以配置在事务为true和false期间你是否需要缓存。对在事务期间设置为false的缓存,将在每个事务开始的时候调用ejbLoad。
比较
对于CMP,总要有附加处理过程的,这是由于在EJB容器以及容器层上(合成码)有一个集成,该容器层用于处理事务、安全、池态、生命周期管理、failover、缓存和关系。这里。CMP(由设计或由规范)必须做一些内部操作(ejbLoad, ejbStore, ejbActivate, ejbPassivate)就像反对JDBC逻辑而由开发者自己手写编程。容器基础结构的好处是优化并生成数据库访问;加速开发以及简化代码维护。
在我见过的基准上,除非数据是被缓存了的, [stateless session bean + DAO (做 JDBC访问)] 执行比实体bean执行快30-50%。
什么时候使用什么
实体bean不能用来取代编写JDBC。如果你的对象-关系模型不是极度复杂(包括许多连接,等等)和灵活并且对于工程代码的维护比速度更重要时可以使用实体beans。
使用JDBC于简单的,原子级别绑定的更新;使存储过程和触发器相结合;并处理大量的ResultSets(记录集)。
WebLogic附加的功能例如read-mostly设计模式和开放式缓存cache-between-transactions设置成true是两种设计选择,它使实体beans成为吸引人的选择。它们都是BEA所特有的属性,在WebLogic特殊部署描述符中指定的。当转移到另一个适用J2EE的应用服务器上时无需代码变更。对于应用来说通过使用开放式缓存并行策略可以在短时间读取旧的数据。
对于大多数在读取而少数时候更新的用例,使用read-mostly设计模式。这种设计模式也有缺点,就是用户会读取到过时的数据。对于一些用户完全不应读取过时的数据的情况,他们可以从读写bean读取。
CMP实体beans的只读实体beans的特征将在以后发布的EJB规范版本中有所提及。使用只读实体beans来实现一个可以周期性更新的分布式缓存。
结论
现实世界中大多数的应用程序的读操作比写操作多很多。现实中read-mostly模式对两种情况来说都将是最好的方法。它提供简单的开发和灵活的部署,并且对于存取数据提供卓越的性能特性,只有当数据被修改时才会有的额外开销。cache-between-transactions设置成true的开放式的并行可以比JDBC还快。编写最优化的SQL对于正规的Java程序员来说很难,所以不要忽略实体beans的价值并决定它们是否最适合。
实体Beans的例子
提供的示例在BEA-HOME/samples/server/src/ejb20子目录下。
论证关系的示例参看BEA-HOME/samples/ server/src/ejb20/relationships/bands目录。
论证多表映射的例子参看BEA-HOME/ samples/server/src/ejb20/multitable 目录。
使用数据库并行选项在weblogic-ejb-jar.xml中指明Database
使用只读并行选项在weblogic-ejb-jar.xml中指明Read-only
使用开放式并行cache-between-transactions并且检查改进的选项在weblogic-ejb- jar.xml中指明Optimistic True并且在weblogic-cmp-rdbms -jar.xml中指明Modified。
实现"read-mostly"设计模式注册bean的实现就像两个EJBs,一个作为只读另一个作为可读写的,并且在weblogic-ejb -jar.xml中ejbname是只读实体bean名字处添加ejbname 。