在 WebSphere Application Server V5 中共享连接

来源:互联网 发布:状态模式 java 编辑:程序博客网 时间:2024/05/16 12:51
 

级别: 初级

Teresa Kan (teresa.kan@us.ibm.com), IBM WebSphere Architecture and Development
Jian Tang (jian.tang@us.ibm.com), IBM WebSphere Development

2004 年 6 月 01 日

本文描述如何在 WebSphere Application Server V5 中共享连接,并展示不同的应用程序(比如 Web 应用程序、session EJB、BMP EJB 和 CMP EJB)之间的编程模型。

引言

在 IBM WebSphere Application Server V5(V5.0, V5.0.1, V5.0.2)中共享连接能够减少资源分配损失和死锁情况。本文将描述如何在 WebSphere Application Server V5 中共享和重用连接以及共享和不共享连接的优缺点。本文还展示在不同的应用程序(例如 Web 应用程序、会话 EJB、BMP EJB 和 CMP EJB)间共享连接的编程模型。此外,一个 WebSphere Studio Application Developer 示例将展示如何在资源引用中设置访问意图(access intent)和隔离级别(isolation level)。

Java 2 Enterprise Edition(J2EE)1.3 规范介绍了一个新的概念: 资源共享作用域(resource sharing scope),由应用程序部署描述符中的部署描述符元素 res-sharing-scope所标识。J2EE 应用程序组件可以使用此部署描述符元素来指示到资源管理器的一个连接是可共享(shareable)或不可共享(unshareable)的。如果将 res-sharing-scope 的值设为不可共享,那么从此资源获取的连接就是不可共享的。否则,从此资源获取的连接就是可共享的。

WebSphere Application Server(以下简称为 Application Server)V5.0.x(包括 V5.0、V5.0.1、V5.02)是符合 J2EE 1.3 的应用程序服务器 。它同时支持可共享连接和不可共享连接。

术语

J2EE 应用程序中使用 连接句柄(connection handle)。它不是到后端资源管理器的物理连接;而是物理连接的一个代理。 物理连接(physical connection)是从 JDBC 驱动程序返回的连接对象。您可以将连接句柄设想为物理连接的一个包装对象。包含"共享的连接(shared connections)"表示包含多个共享同一物理连接的连接句柄。

访问一个标记为 unshareable的资源意味着在组件正在使用的连接句柄和该句柄的物理连接之间有一对一的关系。此访问隐含每次调用 getConnection 都返回一个连接句柄,该连接句柄的物理连接单独用于请求用户。通常情况下,如果该连接导致正在共享此连接的其他应用程序的不可预期的行为(例如:更改隔离级别),您就必须选择不可共享。

将资源标记为 shareable将允许更高的可伸缩性。与在每次调用 getConnection 时重建另一个物理连接不同,如果符合某些条件,则由多个连接句柄共享物理连接。然而,共享一个连接意味着每个用户都不能更改此连接或打断某个共享伙伴(例如:更改隔离级别)。

下一部分"共享物理连接"将描述使获得的连接句柄共享同一物理连接的必要条件。 编程模型部分将讨论共享连接的优缺点并从一个编程模型透视图来描述不同的应用程序模块如何共享同一物理连接,以及共享如何影响编程模型。 设置资源引用隔离级别部分将展示如何设置 res-sharing-scope 的示例,该示例还说明在创建资源引用时如何设置事务隔离级别,以及如何为应用程序设置访问意图。





回页首

共享物理连接

共享一个连接只能在共享作用域中发生。最常见的共享作用域是多个活动的连接句柄可以共享同一物理连接的事务作用域。在 Application Server V5 中有两个事务作用域:全局事务和本地事务容器(Local Transaction Containment,LTC)边界。

  • 全局事务可以是由 J2EE 应用程序启动的用户事务,或者是由 Application Server Enterprise Java Bean(EJB)容器所启动的容器全局事务。当使用 Required 或 RequiresNew 事务属性配置了 EJB 方法时,EJB 容器就启动一个全局事务。当使用 Required、Supports 或 Mandatory 事务属性配置了 EJB 方法时,EJB 容器也可以使用客户端的全局事务。注意 EJB 的客户端可以是 J2EE 应用程序、Servlet、JSP 或另一个 EJB。
  • LTC 边界与资源管理器本地事务(Resource Manager Local Transaction,RMLT)是有区别的,后者是本地事务的资源视图。LTC 是一个作用域,在它里面可以访问多个 RMLT。在没有全局事务的时候,通常由容器来建立 LTC。LTC 的一个示例是含事务属性 NotSupported 的 Bean 方法的执行。 RMLT 的一个示例是将 autocommit 设置为 false,稍后再提交连接。

在 Application Server 中,连接共享作用域是全局事务。虽然在 LTC 中调用多次 getConnection 可以返回具有同一物理连接的多个连接句柄,该物理连接被连续重用,但是并没有被共享。此行为是"连接串行重用(connection serial reuse)。"后面将讨论 LTC 中的连接串行重用。

正如上面所提到的,如果一个应用程序的部署描述符元素 res-sharing-scope 被设置为 shareable,并且符合某些条件,那么在此应用程序中多次调用 getConnection 符合共享同一连接的条件。要让一个共享作用域中调用多次 getConnection 返回同一物理连接,下列连接特性必须一致:

  • Java 命名和目录接口(Java Naming and Directory Interface,JNDI)名称。虽然 JNDI 名称实际上不是一个连接特性,但是此必要条件表示您只能共享来自位于相同服务器进程的相同数据源的连接。您必须在应用程序服务器中的相同数据源上调用两次 getConnection 调用以共享一个物理连接。
  • 资源认证设置。使用资源引用的资源认证设置来指定应用程序组件供应者如何将一个主体(用户 ID 和密码)联系到一个资源管理器。对于非 CMP 应用程序,资源认证由 res-auth 部署描述符来标识。它的值是 Application 或 Container。Application Server 也为 CMP 连接工厂的资源认证提供一个 IBM 扩展。该设置由 IBM 部署描述符扩展文件 ibm-ejb-jar-ext.xmi 中的 resAuth 元素来标识。您可以将 resAuth 的值设置为 Per_Connection_Factory 或 Container。Per_Connection_Factory 与 res-auth 部署描述符的 Application 值一致。当两个数据源或连接工厂引用与 Application Server 中的一个数据源相关联,那么在(并且仅在)这两个引用拥有相同的资源认证值的情况下连接是可共享的。要查阅有关资源认证的描述,请参考 WebSphere Application Server V5 中的数据库验证。
  • 主体。相同的主题表示相同的用户和密码对于相关的数据库是必需的。如果应用程序使用一个不同的用户 ID 和密码来获取连接,那么从 getConnection 调用返回的物理连接将是不同的。当应用程序的资源认证设置被设置为 Application 时,您可以通过 getConnection()getConnection(String userid, String password) 来获取连接。请注意,即使与数据源相关联的受组件管理的认证别名在所使用的 getConnection(String userid, String password) 中有相同的用户 ID 和密码,从这两个调用获取的连接也不是共享的。
  • 连接事务隔离级别特性。在 Application Server 中,您可以通过几种方法来指定获取的连接所使用的隔离级别:
    • 您可以使用访问意图来指定事务隔离级别。
    • 您可以使用 IBM 扩展(在 IBM 扩展文件中指定为 isolationLevel)来指定特定应用程序的数据源引用的隔离级别。
    • 您可以将事务隔离级别设置为 IBM 扩展 API(JDBCConnectionSpec),然后使用 WSDataSource.getConnection(JDBCConnectionSpec)来获取连接。要共享同一物理连接,在一个给定事务中的多个 getConnection 调用的事务隔离级别必须是相同的。请注意,IBM 扩展 API-JDBCConnectionSpec 是一个私有 API。如果您使用这个 API,您的应用程序将不能转换到其他应用程序服务器。
  • 连接 readOnly、catalog 和 typeMap 特性。正如同连接事务隔离级别,这三个连接特性对多个 getConnection 调用必须是相同的,以共享同一物理连接。应用程序可以使用 JDBCConnectionSpec 来指定这三个特性的值。

如果上述必要条件中的一个没有满足,那么两个 getConnection 调用将返回不同的物理连接。

清单 1 显示了在一个用户事务中共享同一物理连接的两个连接句柄。资源引用的资源共享作用域被设为 Shareable。连接 conn1 和 conn2 是从同一数据源 ds 获取的两个连接句柄。由于这两个连接是在一个共享作用域(在本例中,是一个用户事务)中获取的,并且所有的共享必要条件都已满足,所以 conn1 和 conn2 共享同一物理连接。

如果将一个应用程序的部署描述符中的 res-sharing-scope 部署描述符元素设置为 Unshareable,那么这些连接不共享同一物理连接。在一个全局事务或 LTC 中的多个 getConnection 调用将返回包含不同物理连接的连接句柄。



清单 1. 在一个用户事务中共享连接
// Start a user transactionuserTransaction.begin();// Get the first connectionjava.sql.Connection conn1 = ds.getConnection();java.sql.Statement stmt1 = conn1.createStatement();...// Get the second connectionjava.sql.conenction conn2 = ds.getConnection();java.sql.Statement stmt2 = conn2.createStatement();...// Commit the user transactionuserTransaction.commit();

LTC 中的连接串行重用

在一个本地事务容器(Local Transaction Containment,LTC)边界中,您不能拥有多个使用同一物理连接的活动连接句柄。然而,如果前面的包装相同物理连接的连接句柄已关闭,那么您可以重用该物理连接。在您使用如下模式时将发生连接重用:

获得连接 1 ->使用连接 1 ->提交/回滚连接1(可选) ->关闭连接 1 ->获得连接 2 ->使用连接 2 ->提交/回滚连接 2(可选) ->关闭连接 2 -> ...

假设使用了相同的特性并且没有将 res-sharing-scope 设置为 unshareable,那么从第二个 getConnection 返回的连接与从第一个 getConnection 返回的连接相同。因为连接的使用是串行的,在同一时间仅有一个到基础物理连接的连接句柄,所以没有发生真正的连接共享。因此,物理连接被"串行重用(serially reused)。" 请注意,在本模式中提交和回滚连接是可选的。也就是说,即使第一个连接没有被提交或回滚,一旦前一连接已关闭,第二个 getConnection 仍然返回相同的物理连接。您必须注意下列语句隐藏的事务语义:"连接句柄 2 的回滚或提交将回滚或提交对连接句柄 2 所做的工作。它也将回滚或提交对连接句柄 1 所做的工作。"

清单 2 显示了在一个 LTC 中两个连接句柄重用相同的物理连接。该代码在一个 Web 应用程序(Servlet)中,或者位于在全局事务边界内未执行的 EJB 方法中。在没有全局事务边界的情况下,将建立 LTC 边界。在本应用程序中,连接 conn1 从数据源 ds 获得,然后连接 conn1 的 autocommit 值被设为 false。再将记录 1 插入到数据库中。在此之后,没有经过提交或回滚连接,连接 conn1 就被关闭了。然后从相同的数据库 ds 获得连接 conn2。由于用来获得连接 conn2 的连接特性与用来获得连接 conn1 的连接特性相同,并且连接 conn1 已关闭,所以连接 conn2 重用相同的物理连接。在此之后,conn2 的 autocommit 被设置为 false(重用连接,所以不需要将 autocommit 设置为 false,因为它已经是 false),然后记录 2 被插入到数据库中。当回滚连接 conn2 时,不仅回滚了记录 2 插入,同时也回滚了记录 1 插入。



清单 2. 在 LTC 边界中重用连接
// Get the first connectionjava.sql.Connection conn1 = ds.getConnection();conn1.setAutoCommit(false)java.sql.Statement stmt1 = conn1.createStatement();//use statement1  to  insert record 1...  ...conn1.close();java.sql.conenction conn2 = ds.getConnection();conn2.setAutoCommit(false);java.sql.Statement stmt2 = conn2.createStatement();//use statement 2 to insert record 2...  ...conn2.rollback();conn2.close();





回页首

编程模型

共享连接使应用程序更具有可伸缩性。它减少了资源分配,还减少了出现死锁情况的机会。然而,在共享连接时是有限制的。例如,不允许在可共享的连接上设置特性,这是因为一个连接句柄的用户可能无法预见由其他连接句柄做出的更改。该限制是 J2EE 1.3 标准的一部分。

在某些情况下,您不希望应用程序共享物理连接。一个原因在于该应用程序可能需要更改连接属性,而那些更改对于可共享连接是不被允许的。然而,除了增加资源分配消耗之外,还有其他有关使用不可共享连接的约束。

让我们在此查看 清单 1中的代码。假设数据源的资源引用被标记为 Unshareable。连接句柄 conn1 和 conn2 将有不同的物理连接。该应用程序在下列两种情况下可能失败:

  • 如果数据源不支持 XA 事务,例如,数据源是诸如 javax.sql.ConnectionPoolDataSource 的实现这样的非 XA 可能的数据源,该应用程序会失败,这是因为不允许两个非 XA 资源加入到一个全局事务中。Application Server 抛出如下异常:"Method enlist caught java.lang.IllegalStateException (specific exception stack trace) while trying to enlist resources from datasource xxx with the Transaction Manager for the current transaction, and threw an Exception."
  • 即使数据源支持 XA 事务,还会有死锁的可能。例如,stmt1 试图插入一个记录,而 stmt2 试图更新同一记录。当连接的事务隔离级别是 TRANSACTION_REPEATABLE_READ 时,将导致数据库死锁。在这种情况下,您将看到从数据库后端抛出一个表示死锁或事务超时的异常,或在事务超时后看到 javax.transaction.TransactionRollbackedException

我们推荐在 Application Server 中使用可共享的连接。有几种方法确保这些 EJB 和 Web 应用程序共享同一物理连接。要共享连接,请把 res-sharing-scope 部署描述符设为 shareable。请注意,CMP EJB 不包含该部署描述符。Application Server V5 中的 CMP EJB 所使用的连接通常都是可共享的。要在一个共享作用域中共享连接,请使用相同的连接特性。在这些连接特性中,事务隔离级别是常常用到的一个特性。

在获取连接之前有几种方法可以指定事务隔离级别。对于 Bean 管理的持久性(Bean Managed Persistence,BMP)和容器管理的持久性(Container Managed Persistence,CMP)EJB,您可以使用管理意图来指定事务隔离级别。对于 BMP EJB、session EJB 和 Web 应用程序,您可以使用 IBM 资源隔离级别扩展来指定与资源引用相关联的隔离级别。对于任意 JDBC 应用程序(包括 BMP EJB、session EJB 和 Web 应用程序),您可以将事务隔离级别设置为一个 JDBCConnectionSpec 对象,或使用相同的资源引用来获取连接。以下部分将描述如何使用这些方案选择在不同的应用程序之间共享连接。

在 session EJB、BMP EJB 和 Web 应用程序之间共享连接

Session EJB、BMP EJB 和 Web 应用程序(Servlet)符合 JDBC 编程模型。在这些模型之间共享连接按如下发生:

  • 所有这些应用程序访问相同的数据源引用,该数据源引用包含相同的 IBM 隔离级别扩展配置或不包含 IBM 隔离级别扩展配置。当应用程序在一个共享作用域中使用相同的用户 ID 和密码来调用 getConnection()或 getConnection(userid, password)时,返回的连接句柄将共享同一物理连接。
  • 应用程序也可以使用带有同等 JDBCConnectionSpec 对象的 IBM 扩展 WSDataSource.getConnection(JDBCConnectionSpec) 来获得连接。这些调用能够返回在一个共享作用域中的包含同一物理连接的多个连接句柄。该 API 是一个 IBM 扩展 API。如果您的应用程序使用该 API,那么您的应用程序将不能转换到其他应用程序服务器。

在 CMP Bean 或方法之间共享连接

当两个 CMP Bean 方法使用相同的访问意图时,它们就共享在一个共享作用域中的同一物理连接。不同的访问意图策略触发不同的物理连接分配。例如,某个 CMP Bean 包含两个方法:方法 1 与 wsPessimisticUpdate 意图关联,而方法 2 与 wsOptimisticUpdate 访问意图关联。在此情况下,方法 1 和 2 可以不必共享一个共享作用域中的同一物理连接。换句话说,一个 XA 数据源被要求运行于一个全局事务中。如果两个方法都试图访问同一张表,您就可能遇到来自数据库的死锁。因此,共享一个连接是由在 CMP 方法中定义的访问意图所决定的。

在 CMP 和 BMP Bean 之间共享连接

要和 CMP Bean 共享物理连接,请确保您有相同的连接特性。除了隔离级别,CMP Bean 从不在连接中指定 ReadOnly、TypeMap 和 Catalog 特性。如果您想要和 CMP Bean 共享物理连接,您就不能修改 BMP 连接中的这些属性。

如果一个 CMP EJB 和一个 BMP EJB 访问相同的数据源并使用相同的数据库认证,那么有三种方法来确保它们共享同一物理连接:

  1. 未经优化的方案:如果您在 CMP EJB 和 BMP EJB 上不指定任何访问意图,而且您没有为 BMP EJB 所使用的数据源指定资源引用隔离级别,那么当您使用 BMP EJB 中的 getConnection() 方法时,连接特性将是相同的。如果 CMP EJB 和 BMP EJB 在相同的全局事务中运行,它们就共享一个连接。
  2. 在 CMP 和 BMP EJB 的方法中定义相同的访问意图:由于两个方法使用相同的访问意图,所以它们共享一个共享作用域中的同一物理连接。然而,该 BMP EJB 是不可移植的,这是因为它使用了 IBM 扩展 API 来联系一个访问意图。清单 3 显示了 BMP EJB 应用程序使用访问意图来获取连接的示例。在 BMP EJB 应用程序中,您需要做到:
    • 查找一个具体的访问意图服务:aiService。
    • 从该访问意图服务中获得访问意图。
    • 从该访问意图中获得事务隔离级别。
    • 创建一个 JDBCConnectionSpec 对象 connSpec 并设置它的事务隔离级别。
    • 使用该 JDBCConnectionSpec 对象来获取连接。

      清单 3 中显示的代码实现了后端透明性,这是因为它从访问意图中检索事务隔离级别。您的应用程序可以硬编码事务隔离级别,但是不能把它移植到其他后端,因为相同的访问意图对于不同的数据库后端会有不同的事务隔离级别(参见下表 1)。下一个方法是最优越的方法。

  3. 确定一个 CMP EJB 方法使用访问意图的什么隔离级别,然后使用 IBM 隔离级别扩展来指定与资源引用联系的相应隔离级别。一个特别的访问意图包含特定的事务隔离级别。表 1 为不同的数据库后端列出相应的事务隔离级别。BMP EJB 能够在它的资源引用中设置事务隔离级别。如果该事务隔离级别与 CMP EJB 的访问意图中的事务隔离级别相同,那么这两个连接将共享在一个共享作用域中的同一物理连接。这个方法需要更多的手动处理,而且不同的数据库的隔离级别可能不一样。然而,这个方法相对方法 2 来说提供了更有效并可移植的机制。


清单 3. 使用 IBM 扩展 API 在 CMP EJB 和 BMP EJB 之间共享连接
// Look up the access intent serviceAccessIntentService aiService = (AccessIntentService) getInitialContext().lookup(     "java:comp/websphere/AppProfile/AccessIntentService");// Get access intent from the access intent serviceAccessIntent intent = aiService.getAccessIntent(myEntityCtx);// Get the transaction isolation level from the access intentint isoLevel =  WSCallHelper.getDataStoreHelper(ds).getIsolationLevel(intent);// Create a JDBCConnectionSpec object and set the isolation levelJDBCConnectionSpec connSpec = WSRRAFactory.createJDBCConnectionSpec();connSpec.setTransactionIsolation(isoLevel);// Get connection using connection spec Connection conn = ((WSDataSource) ds).getConnection(connSpec);



表 1. 在不同的数据库后端中将访问意图概要映射到事务隔离级别

访问意图概要 DB2 Oracle Sybase Informix Cloudscape SQL Server Select for update WSPessimisticUpdate-WeakestLockAtLoad (default policy) RR RC RR RR RR RR No (Oracle-yes) WSPessimisticUpdate RR RC RR RR RR RR Yes WSPessimisticRead RR RC RR RR RR RR No WSOptimisticUpdate RC RC RC RC RC RC No WSOptimisticRead RC RC RC RC RC RC No WSPessimisticUpdate-NoCollistions RC RC RC RC RC RC No WSPessimisticUpdate-Exclusive S S S S S S Yes
  • RC = JDBC 读操作已提交(Read committed),RR = JDBC 可重复读(Repeatable read),S = JDBC 可序列化(Serializable)
  • Oracle 不支持 JDBC 可重复读(RR)。因此,在 Oracle 上,wsPessimisticUpdate-weakestLockAtLoad 与 wsPessimisticUpdate 的表现和执行与 wsPessismisticRead 和 wsOptimisticRead 相同。由于一个 Oracle 限制,包含 S 事务隔离级别的 OracleXADataSource JDBC 类不能运行。不要在 OracleXADataSource 上使用 WSPessimisticUpdate-Exclusive 概要。
  • ForUpdate 列表示 Select 语句是否将为每个概要包含一个 ForUpdate 子句。

在 CMP 和 JDBC 应用程序之间共享连接

在 CMP 和 BMP Bean 方法之间共享时使用了相同的方法。您可以决定在一个 CMP EJB 方法上的访问意图中使用什么隔离级别,然后使用 IBM 隔离级别扩展来指定与资源引用相应的隔离级别。





回页首

设置资源引用隔离级别和访问意图

本节将展示如何设置资源共享作用域、如何设置 IBM 隔离级别扩展,以及如何将访问目的与 BMP 和 CMP EJB 联系起来。

应用程序示例

本文提供的应用程序示例是一个企业应用程序文件: BankCustomerEAR.ear。它包含一个 EJB 文件: BankCustomerEJB.jar 。该文件包含三个 EJB:Session EJB AccountManagement、BMP EJB Customer 和 CMP EJB Account。Session EJB AccountManagement 担任 Web 应用程序或客户端的会话虚包来访问实体 EJB,因此支持远程接口。Customer 和 Account 实体 EJB 仅支持本地接口。一个 Customer EJB 包含 0 个或更多的 Account EJB。Customer EJB 访问表 Customer,而 Account EJB 访问表 Account。图 1 显示了 Customer 和 Account 表的结构。字段 customerId 是 Customer 表的主键,字段 accountId 是 Account 表的主键,而字段 customerId 是 Account 表的外键。



图 1. Customer 和 Account 表
Customer 和 Account 表

所有的 EJB 方法都是用 Required 事务属性配置的。因此,访问会话 Bean AccountManagement 中的业务方法的实体 EJB 都在一个全局事务中执行。

假设应用程序需要模拟如下场景。一个顾客最多可以有三个帐户。如果一个顾客已经拥有三个帐户,而他试图再创建一个新的帐户,那么该请求将被拒绝。

当一个顾客尝试创建一个新的帐户时,系统将调用 Session EJB AccountManagentWhen 的 createAccount 方法。该方法首先取得可用帐户的数目。如果数目小于 3,就创建该帐户。如果数目大于或等于 3,它将抛出一个异常。

会话 Bean 通过调用 Customer BMP EJB 中的 getNumberOfBAccounts() 方法来取得顾客的帐户数目;清单 4 显示了 AccountManagement 会话 Bean 的 createAccount 方法的代码片断。



清单 4. AccountManagement 会话 Bean 的 createAccount 方法的代码片断
public void createAccount(String customerId, String accountId, double balance)        throws CreateException, FinderException, EJBException {        CustomerLocal customer = customerHome.findByPrimaryKey(new CustomerKey(customerId));        if (customer.getNumberOfAccounts() < ACCOUNT_NUMBER_LIMIT){            accountHome.create(accountId, customerId, balance);        }        else {            throw new EJBException("The maximum number of accounts has been reached.");        }        

假定 Account CMP EJB 由访问意图 WSPessimisticUpdate 来配置。因为是选择 DB2 作为数据库,所以 Account CMP EJB 使用的连接包含事务隔离级别 REPEATABLE_READ。

在 Customer BMP EJB 的 getNumberOfAccounts()方法中,对 Account 表执行一个查询来查找顾客的帐户数目。该查询是 SELECT count(accountId) FROM Account WHERE customerId = ?

正如前面部分所讨论的,要为 Customer BMP EJB 和 Account CMP EJB 共享同一连接,事务隔离级别的连接句柄必须相同。在会话 EJB AccountManagement 的 createAccount 方法中,两个 EJB 都被使用并且都访问相同的 table-Account 表。确保 BMP EJB 和 CMP EJB 共享物理连接是重要的。否则将发生死锁。

考虑 Customer BMP EJB 的数据源引用由 SERIALIZABLE 事务隔离级别配置的情况。getNumberOfAccounts()中使用的物理连接与 Account CMP EJB 的方法中使用的连接将是不同的,这是因为 Account CMP EJB 中使用的连接包含 REPEATABLE_READ 事务隔离级别。在执行 createAccount 方法时将发生死锁,这是因为在同一事务中,您取得两个包含不同隔离级别的两个连接,而且这两个连接访问相同的表 Account。您将看到一个 javax.transaction.TransactionRollbackedException 或一个事务超时异常。

要让应用程序工作,只需用 REPEATABLE_READ 事务隔离级别来配置 Customer BMP EJB 的数据源引用。

要用同一个应用程序来测试死锁情况和工作情况,请使用 REPEATABLE_READ 事务隔离级别来为工作情况配置数据源引用,并用 SERIALIZABLE 事务隔离级别来为死锁情况配置数据源引用。

接下来的部分将展示如何为 CMP EJB 和 BMP EJB 设置数据源引用的事务隔离级别和访问意图。本示例使用 WebSphere Studio Application Developer V5.1(以下简称为 WebSphere Studio)。此描述也适用于 V5.0。

为数据源引用设置事务隔离级别

下列步骤将展示如何为 Customer EJB 设置事务隔离值:

  1. 使用 File -> Import...来导入 EAR 文件 BankCustomerEAR.jar。
  2. 在 J2EE 透视图中打开 J2EE Hierarchy 视图。在左侧面板,展开 EJB Modules -> BankCustomerEJB,然后双击 BankCustomerEJB
  3. 单击右侧面板底部的 References 选项卡。将出现如图 2 所示的面板。

    图 2. CustomerEJB 模块的 J2EE 层次视图
    CustomerEJB 模块的 J2EE 层次视图
  4. 单击 Bean 清单中显示的 Customer,然后单击清单底部的 Add以添加一个数据源引用。
  5. 在 Add Reference 对话框,通过单击资源引用来添加它。单击 Next 前进到下一个对话框。
  6. 输入名称, jdbc/myDS 。该名称是在您的 Bean 代码中查找的本地 JNDI 名称。此 JNDI 名称不是运行时环境中的数据源的 JNDI 名称。当您安装此应用程序时,将运行时环境中的数据源的 JNDI 名称链接到此数据源引用。在安装过程的运行时环境中,使用 JNDI 名称 jdbc/CustomerDS 来将其绑定到数据源。
  7. 选择 javax.sql.DataSource作为类型,这是因为 Customer BMP EJB 访问一个关系数据库后端。
  8. 在 Authentication 字段中,您可以选择 ContainerApplication作为资源验证类型。在本例中,选择 Container 作为验证类型。Account CMP 的资源验证被设置为 "Per_Container",它与 "Container" 一致(请参阅 Database authentication in WebSphere Application Server V5)。如果您将该字段设置为 "Application",则在 Customer EJB 和 Account EJB 之间将不共享连接,这是由于 res-auth 值不匹配。
  9. 保留 Shareable作为共享作用域的值。如果您选择 Unshareable,您的应用程序将不与其他应用程序共享连接。
  10. 单击 Finish

    图 3. 设置数据源引用的事务隔离级别
    设置数据源引用的事务隔离级别
  11. 在 References 清单中展开 Customer。您会看到如图 3 所示的 WebSphere Studio 的右侧面板。在 References 清单中,在 Customer EJB 下面有一个 ResourceRef jdbc/myDS。在该清单的右侧,您能够看到 Sharing scope 字段有一个值 Shareable
  12. 在 WebSphere Bindings 部分的下面,输入 jdbc/CustomerDS 作为 JNDI 名称。该 JNDI 名称是运行时环境中的数据源 JNDI 名称。
  13. 对于工作情况,从 isolation level下拉列表中选择 TRANSACTION_REPEATABLE_READ。任何从该数据源获得的连接都将包含设置为 REPEATABLE_READ 的事务隔离级别。否则,从该清单中为死锁情况选择 TRANSACTION_SERIALIZABLE。图 3 显示了引起死锁的事务隔离级别。
  14. 保存部署描述符。

    如果您单击 Source 选项卡(它与 EJB Jar 文件中的 META-INF/ejb-jar.xml 文件一致),您会在实体 EJB Customer 下面看到如清单 5 所示的下列 <resource-ref> 部分。



清单 5. ejb-jar.xml 中的 <resource-ref> 部分
<entity id="Customer">... ...<resource-ref id="ResourceRef_1078633874297"><description></description><res-ref-name>jdbc/myDS</res-ref-name><res-type>javax.sql.DataSource</res-type><res-auth>Container</res-auth><res-sharing-scope>Shareable</res-sharing-scope></resource-ref>

如果您切换到 Project Navigator 视图,然后打开文件 BankCustomerEJB/ejbModule/META-INF/ibm-ejb-jar-ext.xmi ,您将看到以下 <resourceRefExtensions> 部分,如清单 6 中所示。您会看到隔离级别被设置为 SERIALIZABLE。



清单 6. META-INF/ibm-ejb-jar-ext.xmi 中的 <resourceRefExtensions> 部分
<ejbExtensions xmi:type="ejbext:EntityExtension" xmi:id="EntityExtension_1067213629996">     <enterpriseBean xmi:type="ejb:Entity" href="META-INF/ejb-jar.xml#Customer"/>    <resourceRefExtensions xmi:id="ResourceRefExtension_1078633874417"         isolationLevel="TRANSACTION_SERIALIZABLE">      <resourceRef href="META-INF/ejb-jar.xml#ResourceRef_1078633874297"/>    </resourceRefExtensions>  </ejbExtensions> 

您已为 Customer BMP EJB 成功创建了一个称为 jdbc/myDS 的数据源引用,并将该数据源引用的事务隔离级别设置为 TRANSACTION_SERIALIZABLE

为 Account CMP EJB 设置访问意图

正如在 应用程序示例中所描述的,Account CMP EJB 将它的访问意图设置为 wsPessimisticUpdate 。下列步骤显示如何为 CMP EJB 设置访问意图:

  1. 单击右侧面板底部的 Access选项卡。
  2. 在 Entities 2.x(Bean Level)的 Default Access Intent 清单中,您会看到两个 EJB:Account 和 Customer。单击清单中的 Account EJB,然后单击位于清单右侧的 Add 按钮。

    图 4. 为 Account CMP EJB 设置访问意图
    为 Account CMP EJB 设置访问意图
  3. 将出现如图 4 所示的 Add Access Intent 对话框。您可以从下拉菜单中选择 wsPessimisticUpdate,然后单击 Finish
  4. 保存部署描述符。

    如果您切换到 Project Navigator 视图,然后打开文件 BankCustomerEJB/ejbModule/META-INF/ibm-ejb-jar-bnd.xmi ,您会看到如下 <appliedAccessIntents> 部分,如清单 7 所示。您能够看到访问意图名称是 wsPessimisticUpdate



清单 7. META-INF/ibm-ejb-jar-bnd.xmi 中的 <appliedAccessIntents> 部分
<appliedAccessIntents xmi:id="AppliedAccessIntent_1078634268363"     name="com.ibm.websphere.ejbquery.Default" description="" accessIntentName="wsPessimisticUpdate">    <methodElements xmi:id="MethodElement_1078634268363" type="Unspecified">      <enterpriseBean xmi:type="ejb:ContainerManagedEntity" href="META-INF/ejb-jar.xml#Account"/>    </methodElements>  </appliedAccessIntents>

您已成功地将 Account CMP EJB 的访问意图设置为 wsPessimisticUpdate。由于您将它设置在 Bean 级别,所以 Account CMP EJB 的所有方法都使用此访问意图。

您可以安装 BankCustomerEAR 应用程序并查看访问意图和隔离级别如何影响连接共享。要了解更多细节,请参阅 BankCustomerEAR.ear 文件 中的 readme.txt 。当您运行 BankCustomer 应用程序时,您收到一个死锁异常。遵循 为数据源引用设置事务隔离级别中的步骤将事务隔离级别更改为 REPEATABLE_READ,然后再次运行该应用程序。该应用程序将成功运行。





回页首

结束语

本文通过展示不同的编程模型和示例来关注如何在 J2EE 应用程序中共享连接。共享连接可以减少连接池的大小和避免死锁。然而,共享连接存在缺点。例如,您不能更改您应用程序中的已共享连接的连接属性。您需要根据如何实现您的应用程序来决定是否共享连接。






回页首

下载

名字 大小 下载方法 BankCustomerEAR.ear 18KB  FTP|HTTP 关于下载方法的信息 Get Adobe® Reader®

参考资料

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文.

  • WebSphere Application Server V5 中的数据库验证

  • J2EE 1.3 规范

  • EJB 2.0 规范(最终发行版)

  • WebSphere Application Server Information Center V5


作者简介

 

Teresa Kan是 IBM WebSphere Application Server 软件开发区的团队负责人和架构师。她负责设计 Relational Resource Adapter,这一软件能为 WebSphere J2EE 应用程序提供持久机制来访问关系数据库。Teresa 拥有犹他州立大学计算机科学的理工硕士学位。


 

Jian Tang在 WebSphere Application Server Relational Resource Adapter 团队工作,该团队为 WebSphere 应用程序提供相关的数据库连通性。Jian 拥有内布拉斯加州——林肯大学的硕士学位。