IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  WebSphere  >

IBM WebSphere 开发者技术期刊: 理解 WebSphere Application Server EJB 的访问意图

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 初级

Mattias Persson (mpersson@uk.ibm.com), 软件工程师

2004 年 9 月 01 日

WebSphere®Application Server 定义了名为访问意图策略的一组扩展用来管理对实体 bean 的访问。在调整 EJB™ 应用程序的性能时,了解这些策略的特征以及运行方式是非常重要的。本文将论述怎样在 WebSphere Studio Application Developer V5.1.1 中配置访问意图。

引言

Java™2 Enterprise Edition (J2EE) 应用服务器的高度多线程执行概要能允许同时激活多个事务。与这些事务相关的持久应用程序的数据由相关数据库管理系统(RDBMS)来管理。当被事务范围中的应用程序需要时,数据库中的应用程序数据会被载入由容器管理的 Enterprise JavaBeans™ (EJB)中。在请求读访问和请求更新访问的事务之间产生冲突时,数据能否得到正确的保护是十分重要的。EJB 容器、持久性管理程序、相关资源适配器和数据库将联合起来对并发性(concurrency)进行控制。 应用程序数据的整合是必需的,且由应用程序服务器引擎确保。WebSphere Application Server 应用程序处理这些低级别的详细问题,从而为开发人员减轻负担。

为支持不同的数据库访问方法,WebSphere Application Server 定义了对 EJB 部署描述符的一个扩展,使开发人员能够定制应用程序并发性控制设置。不同的策略被实现为说明性的注解,为 WebSphere 提供如何访问数据的提示。这些策略被称为访问意图(Access Intent)。WebSphere 提供手动配置这些策略的功能,并能够提供良好的性能。事务之间在相关数据库资源上的争夺会成为糟糕的瓶颈,因此调整应用服务器访问持久数据的方式可以提高吞吐量和响应时间,也减少了资源的争夺。

在本文中讨论的访问意图策略只能在容器管理持久性 (CMP) 2.x bean 上应用。我们假定读者具有使用 WebSphere Studio Application Developer 开发 EJB 应用程序的经验。如果你了解 SQL 语言的一些基本知识将对理解本文的内容有所帮助。在接下来的部分,我们将对访问意图策略的各种属性加以讨论。在文章的结束部分,我们会提供一些实例帮助您在 WebSphere Studio Application Developer V5.1.1 中使用访问意图。





回页首


访问意图策略的属性

以下部分将描述 WebSphere Application Server 中访问意图策略的主要组成。访问意图 由特定数据库隔离级别、锁定策略以及某些情况下的预读(read-ahead)提示组成。

每个已定义的访问意图策略都是一组属性的集合,用来控制 EJB 实体的持久性和并发性处理的不同方面。在本文中描述的信息与 WebSphere Application Server V5.1 相关。尽管本文的意图简单涉及有关企业的特殊概念,但 WebSphere Application Server Enterprise Version 5.1 进一步扩展了这里所讨论的概念。

数据库隔离级别

事务处理系统必须支持目前正在运行中的事务和其他事务之间完全的隔离。然而,完全的隔离可能会对性能和吞吐量产生影响。然而如果每个事务都是按连续方式运行,那么多线程应用程序服务器就不能够很好地执行。ANSI SQL-92 标准定义了数据库隔离的四个级别,这样我们可以用来在事务中获得并行性的若干形式。这四个级别依照各自允许的特定数据访问特性的不同(例如,脏读、不可重复读以及虚读)来划分。在接下来的部分,我们不仅会对置于数据库连接上的隔离级有所了解,还将会使用访问意图策略来确定应用服务器是怎样设置这些隔离级的。不同的数据库厂商可以使用不同的技术来设置相应的隔离级。

这四个不同的 JDBC™ 隔离级将在以下列出,且还将对 DB2 怎样处理每个隔离级做出描述。其他的数据库厂商也许在这点上各有不同,在大致思想是类似的。

  • TRANSACTION_SERIALIZABLE
    这是最高级别的隔离级。在DB2 内部的该隔离级被称为可重复读(Repeatable Read)。数据库表格中所有受到目前事务影响的行都将被锁定,其他的事务不能插入、删除或更新该被选集合内的任何一行。如果行的插入有可能改变别的事务使用的选择集,锁定功能也将起作用。例如, SELECT * FROM orders WHERE Total > 2000 锁定了所有表格中的行而不仅仅是那些和判定条件匹配的行。这将防止幻象行(phantom row)的出现。未被允许的来自其他事务的改变将不起作用。

  • TRANSACTION_REPEATABLE_READ
    幻象行会在该隔离级出现。因为 SELECT * FROM orders WHERE Total > 2000 语句只会锁定符合条件的行。如果该语句在事务内重新发行(reissue),结果是不同的。肯定的是除非在事务中对这些行进行的读操作和更新操作被提交,不然被操作的那些行是不能被其他任何事务改变的。可重复读映射到 DB2 的读稳定性(Read Stability)隔离级。

  • TRANSACTION_READ_COMMITTED
    DB2 将此映射为光标稳定性(Cursor Stability)隔离级。如果发行 SELECT * FROM orders WHERE Total > 2000 语句,行只会在结果集被遍历后才会锁定。显而易见,虽然会出现幻象行,但更重要的是不可重复读也会出现。这个隔离级只有当一个事务对同样的数据进行两次读操作而其他事务修改了两次读操作之间的数据,从而导致了每个读操作的结果不同时才会发生。

  • TRANSACTION_READ_UNCOMMITED
    事务之间不是互相隔离的,且能访问彼此没有提交那些修改。目前,WebSphere Application Server 中没有任何访问意图使用了该隔离级。

选择合适的隔离级是很复杂的事情。然而 WebSphere Application Server 中不同的访问意图策略将会为所支持的数据库选择一个合适的隔离级,为开发人员减轻了该细节上的不少麻烦。(参见 参考资料部分。)当其他外部应用程序访问同样的数据库时,了解 WebSphere Application Server 将哪个隔离级置于它和该数据库的连接上是非常重要的。

锁定策略

与隔离级相关联,应用服务器可以使用两种不同的锁定策略来远程控制事务的并发性。

  • 悲观锁定
    悲观锁定策略是事务早期获得的锁定,并一直保持到事务被提交或是回滚。
    图 1. 悲观锁定图解
    悲观锁定场景的图解
    图 1 展示了悲观锁定背后的思想。事务 1(Tx1)将值 1000 从数据库读入 balance 变量。当事务 2(TX2)试图访问包含该 balance 变量的行时,TX2 被阻挡直至 TX1 将该 balance 更新至 3000 并提交了它的变化。然后,TX2 才能继续读值 3000 并将其更新至 4000。
  • 乐观锁定
    悲观锁定方法最主要的问题是,事务之间必须互相等候。避免该问题的方法是变得更乐观些,并认为其它事务并不期望在同一时刻更新同一实体。带着这样的思想,我们可以避免在事务处理一开始就锁定数据。这样,数据只有在事务处理结束及数据被更新的时候才会被锁定。该方法要求确保数据在被读或是被更新时没有改变。这就是 WriteWriteConflict。
    图2. 乐观锁定图解
    乐观锁定场景的图解
    图2展示了乐观锁定背后的思想。与图 1 类似,事务 1(TX1)在读 balance 值 1000 时并没有锁定数据库的行。事务 2(TX2)并没有被禁止做同样的事情,并在最开始的时候便对值 1000 进行了读操作。在提交事务事前,它必须检查冲突并发现现在的值变成了 3000,这和最初读的值不同。这样,事务被异常终止并被重新启动。检查冲突的任务可以通过许多形式实现。在数据库中可以使用额外的一列来保存版本或时间戳(参见 参考资料部分以获取关于冲突处理技术的更多信息)。然后,用条件表达式进行的更新只会在该字段就是数据被读出的那个字段进行。还会有一个或多个字段用来完成同样的意图。每个事务中被更新的必定是一个字段。

乐观锁定的使用能实现更多地同时数据访问。缺点是如果冲突时常发生时,解决冲突所需的花费是昂贵的。因此,只有当冲突并不多见时,才合适使用乐观锁定。如果应用程序访问模式主要是更新数据,那么乐观锁定方法能有利地避免乐观并发性更新的失败。

其他属性

WebSphere Application Server中的访问意图策略还包含了一些其他属性,这些属性只能在可实现策略定制的 Enterprise 版本上才能被更改。其中一个属性是 Collection Increment,它允许定义调用 EJB 实体 multi-finder 方法时返回的集合大小。我们可以简单的将其设置为一个比较低的值,这就意味着当我们迭代该集合时,数据库将反复查询以获得数据。一个比较高的值就意味着尽管维护较大集合的代价更高,但所有的数据可能在一个数据库查询中返回。您可以查阅 WebSphere Application Server 信息中心(参见 参考资料部分)以获取更多的相关信息,以及查阅不同访问意图策略采用的缺省值。WebSphere Application Server 信息中心还对 Collection Scope 和 Resource Manager Prefetch 的概念进行了描述。这些都是以使用 WebSphere Application Server 企业版为前提的。





回页首


访问意图策略

现在,让我们来看看 WebSphere Application Server 上不同的访问意图。

策略的主要标识是锁定策略以及类型。访问意图的类型可以是读取或是更新。如果当您将读策略赋值到实体 bean,那么在试图更新它时就会出现异常。更确切的说,您不能调用任何 setter 方法。考虑将使用哪种隔离级也很有意思,特别是当其他的系统也在访问同一数据库时。要认识到不同数据库提供商之间的数据库隔离级是有差别的,这一点很重要。大部分的实现都会在下面的表格中显示(除了映射到不同隔离级的 Oracle,参见 参考资料部分以了解更多关于不同数据库实现的信息)。表格 1 展示了目前在 WebSphere Application Server V5.1 中定义的七种不同策略 。该数据基于使用 DB2 作为 RDBMS。

表格 1. WebSphere 的访问意图策略

策略 类型 锁定策略 隔离级别
wsPessimisticUpdate更新悲观可重复读
wsPessimisticUpdateWeakestLockAtLoad更新悲观可重复读
wsPessimisticUpdateNoCollision更新悲观读取提交
wsPessimisticUpdateExlusive更新悲观序列化
wsPessimisticRead读取悲观可重复读
wsOptimisticUpdate更新乐观读取提交
wsOptimisticRead读取乐观读取提交

悲观策略

正如先前描述的一样,悲观方法的意图是确保装入实体 bean 的数据在整个事务处理持续时间内处于锁定状态。数据库访问通常会对被查询的数据进行读锁定,在数据更新后会变成更新锁定。我们期望确认当数据从数据库中取出时所有的行都已被锁定。使用一条特殊的 SQL 语句 SELECT..FOR UPDATE 可以完成此项操作。更新子句将确保更新锁定在被选择的行上。直至最先的事务提交了变化后,其他的事务对所有行发行的相同语句的锁定才被解除。WebSphere Application Server V5.1 定义了五种不同的悲观策略。他们拥有通过自身名字来描述的锁定提示,wsPessimistic#hint#。此处的 #hint# 将使用实体 bean 进一步地描述我们的意图。

wsPessimisticUpdate
隔离级将被设置成可重复读(Repeatable Read),且 "SELECT..FOR UPDATE" SQL 语句将用来在数据库中获得更新锁定。如果您想创建真正的悲观场景,这便是要使用的策略。缺点是您不能使用在 finder 方法中定义的复杂查询。这是因为 SELECT..FOR UPDATE 不支持分组和排序。因此您不能创建 finder 来实现从数据库获得一组行并通过指定的列来排序的目的。

wsPessimisticUpdateWeakestLockAtLoad
这是缺省策略。如果在部署描述符中没有访问意图,WebSphere Application Server 将会使用该提示。和 wsPessimisticUpdate 相比,它并不使用 SELECT..FOR UPDATE 子句来获取更新锁定。容器将用能被目标数据库接受的最低级锁定来装载数据。这就表示这些行将被锁定为共享。当尝试获取更新锁定时,该锁定将变为更新锁定。

wsPessimisticUpdateNoCollision
使用读取提交(Uses Read Committed)是更低的隔离级。在装载数据时它并不使用 FOR UPDATE 子句,所以行并不会被锁定。使用该策略时必须确保同一行或多行没有进行同时的更新。如果并发事务用同样的主键更新实体 bean 时,更新资料将会丢失。因此请避免使用该策略。

wsPessimisticUpdateExclusive
这将确保事务对正在更新的数据进行唯一的访问。数据库隔离被设置为序列化(Serializable),且 SELECT..FOR UPDATE 语句在装载数据时使用。读取者和写入者都需等待直至唯一的事务已经提交了它的修改。该策略应该慎用,因为它会在应用程序中造成严重的瓶颈。和 wsPessimisticUpdate 一样,该策略在复杂查询中并不被支持。

wsPessimisticRead
如果您清楚可以对实体 bean 随时进行读操作且实体 bean 不会更新,那么该策略可以模拟只读锁定。在实体 bean 中尝试更新数据会出现异常。请小心谨慎并确保没有其他外部应用程序正在以不同意图访问同一表格。

乐观策略

在实体 bean 被装载时,应用乐观策略使数据不被锁定。取而代之,在实体 bean 在返回数据库时将使用另一条更新语句 UPDATE .. WHERE 。我们将在以下部分了解在 WebSphere Studio 中怎样对此进行配置。

wsOptimisticUpdate
当选择执行乐观锁定策略更新实体 bean 时,这是唯一我们可以使用的策略。它将发行一条常规选择语句来将数据载入实体 bean 以及完成自身的更新操作。之后还将在提交数据时发行另一条更新语句。

wsOptimisticRead
与 wsPessimisticRead 唯一的不同是它将隔离级设置成读取提交。

预读提示

另外,还有一个特征能在访问意图策略中应用,即预读提示。当实体间定义了关联时才能使用该特征。该方法是通过对相关 bean 进行高速缓存来将对数据库的访问减到最小。例如,如果 bean A 、B 还有 C 三者相关联,我们便可在这些相关联系上指定预读提示。那么,当 bean A 被装载时,如果一对一的关系,一个实体 bean C 将被载入;如果是一对多的关系,那么一系列匹配的 bean B 将被载入。预读提示只适用于关于 CMP 2.x 实体 bean 的 findByPrimaryKey 方法。应用服务器会产生一个更复杂的 SQL 查询从多种表格中引入数据。当我们随后对这些 bean 请求访问时,不需要对任何数据库进行访问。


图 3. 具有容器管理关系的实体 bean
具有容器管理关系的实体 bean.

预读提示可以在一个或多个隔离级被定义。如果 D 和 E 相关,而 E 和 F 相关,那么当 D 被装载时会产生一个提示,相应的 E 和 F 实体也被载入。除非相应的容器管理关系(Container Managed Relations,CMR)bean 会在事务处理中被涉及到,否则不要使用预读提示。该提示并不意味着在数据库上额外的花费,因为如果不需要的话表格之间的 join 语句应该避免。





回页首


配置WebSphere Studio 中的访问意图

该部分是关于配置 WebSphere Studio Application Developer Version 5.1.1 中访问意图的简要指南。

策略可以应用于整个 bean 或是 bean 中某一方法中。本文并不是从方法级别的观点论述访问意图策略,这对应用程序将产生复杂的影响。如果不同的隔离级在同一事务中使用,那么很容易导致死锁或阻碍共享连接。

对实体 bean 的访问意图策略信息将在 WebSphere 扩展描述符文件中论述, ibm-ejb-jar-ext.xml 。自然地,意图是您无需编辑该文件,所有的这些都将由 WebSphere Studio 为您完成。我们将举一个简单的应用程序实例来访问产品一览表数据。

例 1. wsOptimisticUpdate

图 4 展示了在使用访问意图时不同类的简图。


图4. 例 1 中的类图
图 4. 示例场景的类图

无状态会话 bean 的 CatalogBrowser 用于获得一系列产品展示给用户。同时,仓库管理员可以在管理界面上更新产品目录。这些都是通过 CatalogAdmin 无状态会话 bean 来完成的。我们知道产品数据通常很少更新,每个月差不多几次而已,而顾客经常需要浏览产品目录。因此,我们可以优化获得产品实体的锁定策略。由于该数据库中出现冲突的概率较低,我们可以放心的使用乐观锁定策略。在 WebSphere Studio Application Developer 的实体 bean 上配置访问意图:

  1. 在 J2EE Hierarchy 中,右键单击包含要配置实体的 EJB 模块,并选择 Open with => Deployment Descriptor
  2. 在主窗口中,选择 Access
  3. 转至 WebSphere Extensions 部分(图 5),选择 Add 显示“Add Access Intent”对话框(图 6)。
    图 5. 部署描述符编辑器:Access 选项卡 -WebSphere Extenstions
    图 5. 部署描述符编辑器:Access 选项卡

    图 6. 添加访问意图对话框
    图 6. 添加访问意图对话框
  4. 从下拉菜单中选择访问意图名。由于本例中的 Product bean 与其他 bean 无关,因此预读提示不可选。
  5. 最后,我们必须指定当修改的数据被提交时在我们的更新语句中应该包含什么。即在 SQL UPDATE 语句中的 WHERE 子句应该包含什么字段。在 Product 实体中需要一个或多个字段来指明已成功的更新。在本例中,可以假设更新可以发生在产品的任何方面,并且在查询时所有的方面都将包含在内。为此您需要为应用程序定义数据库映射。完成此操作后,打开应用程序的映射编辑器(图7)。
    图 7. 产品实体 bean 的映射编辑器
    图 7. 产品实体 bean 的映射编辑器
  6. 对于 CMP 实体 bean 中的每个字段,我们都可以通过将值设定成 true 或 false 来指定它是否做为 OptimisticPredicate 使用。Java 初始字段缺省都设置为 true。由于被竞争事务更新的可能只是名称、描述和种类这些字段而已,因此这不能满足我们的需要。如果更复杂的更新语句不包含这些字段,那些我们就无法确定冲突的发生。

例 2. 使用预读提示的 wsOptimisticRead

现在,我们将对例 1 的类对话框稍微更改一下,这将改变实体 bean 的访问类型。图 8 展示了我们不再用 CatalogAdmin 无状态会话 bean 来更新产品数据。假设产品数据刚从数据库读出且从未更新。我们引进一个名为组件的新实体。产品由一个或多个组件通过一对多的关系组成。


图 8. 例 2 的类图
图 8. 示例场景的简单图示

由于产品和组件实体都没更新,读访问类型将得到使用。从而我们可以使用 wsOptimisticRead(图 8)来代替。当产品实体装载时,最好相应的组件也能被读取。请按照例 1 的步骤设置访问意图。由于我们不需要更新任何方面,所以不需要定义 OptimisticPredicates。通过创建访问意图时检查相应的框来启用预读提示。在此过程中,我们必须指定使用的预装载路径。这将成为相互关系中的组件角色(图 9)。


图 9. 指定预装载路径对话框
图 9. 指定预装载路径对话框




回页首


结束语

本文记述了 EJB 实体中乐观锁定和悲观锁定策略背后的思想,包括有关 WebSphere Application Server 访问意图的不同术语、数据库隔离级、预装载提示和锁定策略。在 WebSphere Application Server 中有七种不同的访问意图策略能通过任一 bean 或方法级别而在 CMP 2.x 实体 bean 的使用。本文中的实例只示范了 bean 级别上的访问意图。除非您对将产生的影响有了充分的准备和认识,否则最好避免使用方法级别的访问意图。

在调整 EJB 应用程序的性能时使用访问意图是很重要的,特别是在对数据库进行调整时。如果一项策略没有被正确使用,则将会引进错误。因此,一般认为如果应用程序的性能已经可以接受,就没有必要调整实体 bean 访问。引进访问意图将使应用程序的设计构思变得不易理解。例如,在 bean 上放置一个读类型的提示,将阻止该 bean 的更新。在多用户开发程序中,必须确保没有人会尝试更新此 bean。然而,如果在运行中碰到了问题且数据库争用非常严重,那么调整访问意图将会对您有所帮助。





回页首


感谢

对 John J Stecher 和 Graham Rawson 对本文提出的批评和建议表示感谢。



参考资料



关于作者

Mattias Persson在英国 Hursley Park IBM Java 技术中心工作。他是 Java 性能研究小组的成员并且他着重于应用服务器环境的测量和优化上。他是通过认证的 Enterprise Architect for J2EE,并在 2002 年获得了瑞士 Vaxjo 大学计算机科学专业的理学硕士学位。毕业之后他便加入了 IBM。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款