使用 Enterprise Bean 处理事务
本节讲述内置到 Sun ONE Application Server 7 的 Enterprise JavaBeans (EJB) 编程模型中的事务支持。
注意
如果不熟悉用 EJB 技术进行事务处理,请参阅 Java 软件教程:
http://java.sun.com/j2ee/docs.html
有关 EJB 事务支持方面的详细信息,请参阅 Enterprise JavaBeans Specification 2.0 第 17 章“事务支持”。
关于 Sun ONE 应用服务器的概述资料,请参阅“ Sun ONE Application Server Enterprise JavaBeans 技术介绍”和 Sun ONE 应用服务器产品介绍。
本节介绍以下主题:
JTA 和 JTS 事务支持
J2EE 包括通过两个规范对分布式事务的支持:
Java 事务 API (JTA)
Java 事务服务 (JTS)
JTA 是一种不受实施限制的高层协议 API,该协议 API 使应用程序和应用服务器可以访问事务。
JTS 指定对事务管理器的实施,该事务管理器以低于 API 的级别支持 JTA 并实施 OMG 对象事务服务 (OTS) 1.1 规范 Java 映射。JTS 使用“互联网 ORB 间协议”传播事务 (IIOP)。
当前事务管理器实施方法支持 JTS 和 JTA。 EJB 容器本身使用 Java 事务 API 接口与 JTS 交互。
J2EE 事务管理器控制所有 EJB 事务(除 Bean 管理 Java 数据库连接 (JBDC) 事务之外),并使 Enterprise Bean 可以在一个事务内更新多个数据库。
关于事务处理
作为开发人员,您可以编写更新多个数据库中数据的应用程序,这些数据库分布在多个站点之间。该站点可以使用不同供应商提供的 EJB 服务器。
本节提供以下主题的概述信息:
平面事务
Enterprise JavaBeans Specification 2.0 需要支持平面(与嵌套相对)事务。在一个平面事务中,每个事务都与系统中其他事务相分离,并独立于其他事务。在当前事务结束之前,不能在同一线程中启动另一事务。
平面事务是最流行的模型,并为多数商用数据库系统所支持。尽管嵌套事务对事务的控制粒度较细,但支持它们的商用数据库系统却少得多。
全局和本地事务
理解全局事务和本地事务之间的区别在理解 Sun ONE 应用服务器对事务的支持时非常重要。
全局事务 - 资源管理器管理和协调的事务,可以跨越多个数据库和进程。资源管理器一般使用 XA 二阶段提交协议与“企业信息系统”(EIS) 或数据库进行交互。
本地事务 - 在单个 EIS 或数据库的本地并且限制在单个进程内的事务。本地事务不涉及多个数据来源。
本地和全局事务都使用 javax.transaction.UserTransaction 接口划分界限,客户端必须使用此接口。本地事务不使用事务管理器,因而处理速度更快。
起初,所有事务都是本地的。如果非 XA 数据源连接是事务范围中登记的第一个资源连接,当“另一个”XA 数据源连接加入此连接时,该非 XA 数据源连接就成为全局事务。如果另一个非 XA 数据源连接试图加入,就会产生异常。
Sun ONE 应用服务器在全局或本地事务模式下工作,但不能在同一事务中混合两种模式。
注意
如果您的应用使用全局事务,则必须配置和启用相应的 Sun ONE 应用服务器资源管理器。有关详细信息,请参阅 Sun ONE 应用服务器管理界面联机帮助和管理员指南。
分界模型
开发人员可以选择使用 EJB 代码中的编程事务分界(Bean 管理),还是使用声名性分界(容器管理)。不管 Enterprise Bean 是使用 Bean 管理还是容器管理事务分界,都是由 EJB 容器和 Sun ONE 应用服务器实施事务管理。容器和服务器实施必需的低级事务协议,如事务管理器和系统或 Sun ONE 消息队列提供者之间的二阶段提交协议、事务上下文传播、分布式二阶段提交等。
以下几节介绍这些分界模型:
容器管理事务
Enterprise Bean 的一个主要优点是支持容器管理事务,也称为声名性事务。在具有容器管理事务的 Enterprise Bean 中,EJB 容器设置事务的边界。
注意
您可以将容器管理事务与任何 Enterprise Bean 类型(会话、实体或消息驱动型)一起使用,但实体 Bean 只能使用容器管理事务。
容器管理事务简化开发,因为 EJB 代码不明确标记事务的边界。即,代码不包括开始和结束事务的语句。容器负责:
划分事务上下文界限,并透明地传播事务上下文
与事务管理器一起,确保事务中的所有参加者都能看到一致的结果
Bean 管理事务
EJB 规范使用 javax.transaction.UserTransaction 支持 Bean 管理事务分界。对于 Bean 管理事务,您必须执行 Java 命名和目录界面 (JNDI) 查找来获取 UserTransaction 对象。
注意
您可以将 Bean 管理事务用于会话或消息驱动型 Bean,但实体 Bean 必须使用容器管理事务。
Bean 管理事务有两种类型:
JDBC 类型 - 您可以使用连接接口的提交和回滚方法划分 JDBC 事务的界限。
JTA 类型 - 您调用 UserTransaction 接口的开始、提交和回滚方法划分 JTA 事务的界限。
提交选项
EBJ 协议可为容器提供在提交事务时选择实例状态处置方式的灵活性。这使容器可以最好地管理如何缓存实体对象的状态以及如何将实体对象标识与 EJB 实例关联。
有三个提交时选项:
选项 A - 容器在事务之间缓存就绪实例。容器确保实例可独占性访问持久性存储中的对象的状态。
在此情况下,容器不必在下一个事务开始时同步实例在持久性存储中的状态。
注意
Sun ONE Application Server 7 不支持提交选项 A。
选项 B - 容器在事务之间缓存就绪实例,但容器不确保实例独占性访问持久性存储中对象的状态。这是默认值。.
在此情况下,容器必须通过在下一个事务开始时从持久性存储中调用 ejbLoad 来同步实例的状态。
选项 C - 容器不在事务之间缓存就绪实例,相反,在一个事务完成之后,将实例返回到可用实例池中。
提交选项 C 下的每个业务方法调用的生命周期看起来如下:
ejbActivate->
ejbLoad ->
business method ->
ejbStore ->
ejbPassivate
如果有多个事务性客户端同时访问同一个实体 EJBObject,第一个客户端获取就绪实例,而后续并行客户端则从池中获取新的实例。
Sun ONE 应用服务器部署描述符有一个元素 commit-option,该元素指定要使用的提交选项。根据指定的提交选项,实例化适当处理程序。
注意
假定使用提交选项 A,开发者负责确保只有此应用正在更新数据库。换句话说,这不是容器的责任。
管理和监视
管理员可以控制 server.xml 文件中 transaction-service 元素的以下整个实例事务服务属性:
automatic-recovery
timeout-in-seconds
tx-log-directory
heuristic-decision
keypoint-interval
log-level
monitoring-enabled
有关这些属性的进一步说明,请参阅 Sun ONE 应用服务器管理员配置文件参考。
另外,管理员可以使用事务管理器中的统计信息监视事务,统计信息包含有关以下活动的信息:服务器启动以来完成/回滚/恢复的事务的数目、目前处理的事务,等等。
有关管理和监视事务的信息,请参阅 Sun ONE 应用服务器管理界面联机帮助以及 Sun ONE 应用服务器管理员指南。
使用容器管理事务
一般来说,容器在启动一个 EJB 方法时立即开始一项事务,并且就在该方法退出时提交事务。每个方法都可以与单个事务关联。
注意
一个方法内不允许有嵌套事务或多个事务。
容器管理事务不需要将所有方法与事务关联。部署 Enterprise Bean 时,您通过设置事务属性,指定 Bean 的哪些方法与事务关联。
尽管具有容器管理事务的 Bean 需要较少代码,但这样的 Bean 也有一个局限性:
执行一个方法时,该方法只能与一个事务关联,或者不与任何事务关联。
如果此局限性使对 Bean 进行编码变得不容易,Bean 管理事务可能是您的最佳选择。
当发生提交时,事务会提示容器 Bean 已经完成其有用工作,并告知容器将其状态与基本数据源关联。容器允许事务完成,然后释放 Bean。与一个提交的事务关联的结果集不再有效。随后对于同一 Bean 的请求使容器发出一项使状态与基本数据源关联的工作。
注意
不明确提交容器启动的事务。
任何参加者都可以回滚事务。
以下各节与使用容器管理事务开发 Enterprise Bean 相关:
指定事务属性
事务属性是一个控制事务范围的参数。
由于事务属性存储在部署描述符,因而可以在 J2EE 应用程序部署的多个阶段对事务属性进行更改:在创建 EJB 时,在汇编(封装)时、或在部署时。但是,EJB 开发人员应负责在创建 EJB 时指定属性。只有在汇编者把组件汇编到大型应用中时,才应修改属性。
注意
不要指望部署 J2EE 应用的人指定事务属性。
您可以针对整个 Enterprise Bean 或针对个别方法指定事务属性。如果已经为某个方法指定一个属性,而为 Bean 指定另一个属性,则为方法指定的属性优先。
提示
如果不能确定如何在 EJB 的部署描述符中设置事务,请指定容器管理事务。然后为整个 Enterprise Bean 设置 Required 事务属性。多数情况下,此方法是有效的。
有关 EJB 部署描述符文件的详细信息,请参阅“创建部署描述符”。
本节介绍以下主题:
不同属性要求
为个别方法指定属性时,要求随 Bean 的类型不同而不同。
会话 Bean - 需要为业务方法定义的属性,但不允许将它们用于创建方法。
实体 Bean - 需要业务、创建、删除和查找程序的事务属性。
消息驱动型 Bean - 需要 onMessage 方法的事务属性(Required 或 NotSupported)。
属性值
一个事务属性可以有以下值之一:
Required
如果客户端正在某个事务内运行,并调用 Enterprise Bean 的方法,则方法在客户端的事务内执行。如果客户端与某个事务没有关联,则容器在运行方法之前,启动一项新事务。
提示
Required 属性对多数事务有效。因此,您可能需要将其用作一个默认值,至少是在开发的早期阶段。由于事务属性是声名性的,因而以后您可以容易地进行更改。
RequiresNew
如果客户端正在某个事务内运行,而且调用 EJB 的方法,容器就采取以下步骤:
挂起客户端的事务。
启动新的事务。
把调用委托给方法。
在方法完成之后回复客户端的事务。
如果客户端不与某个事务关联,容器就在运行方法之前启动一个新的事务。
要想确保方法始终在新的事务内运行,您应使用 RequiresNew 属性。
Mandatory
如果客户端正在某个事务内运行,而且调用 EJB 的方法,该方法就在客户端的室内执行。如果客户端不与某个事务相关联,容器就会产生 TransactionRequiredException。
如果 EJB 的方法必须使用客户端的事务,请使用 Mandatory 属性。
NotSupported
如果客户端正在某个事务内运行,并调用 EJB 的方法,则容器在调用方法之前挂起客户端的事务。方法完成之后,容器恢复客户端的事务。
如果客户端不与某个事务关联,在运行方法之前,容器不会启动新的事务。
Supports
如果客户端在某个事务内运行,并调用 EJB 的方法,该方法就在客户端的事务内执行。如果客户端不与某个事务相关联,运行该方法之前,容器不会启动新的事务。
注意
由于方法事务行为可能变化,所以您应小心使用 Supports 属性。
Never
如果客户端在某个事务内运行,并且调用 Enterprise Bean 的方法,容器就会产生 RemoteException。如果客户端不与某个事务相关联,运行方法之前,容器不启动新的事务。
将 NotSupported 属性用于不需要事务的方法。由于事务调用额外开销,此属性可以提高性能。
下表概述事务属性的影响。左列列出事务属性,中列列出客户端事务的类型,而右列列出业务方法的事务类型。事务可以是 T1、T2 或无。(T1 和 T2 事务都是由容器控制的。)
T1 事务 - 与调用 Enteprise Bean 中的某个方法的客户端关联。多数情况下,客户端是另一个 Enterprise Bean。
T2 事务 - 在执行之前由容器启动。
None - 在第三列中,单词 None 指业务方法不在容器控制的事务内执行。但是这样的业务方法中的数据库调用可能是有数据库的事务管理器控制的。
事务属性和范围
事务属性
客户端的事务
业务方法的事务
Required
无
T2
T1
T1
RequiresNew
无
T2
T1
T2
Mandatory
无
错误
T1
T1
NotSupported
无
无
T1
无
Supports
无
无
T1
T1
Never
无
无
Ti
错误
回滚容器管理事务
回滚容器管理事务的方法有两种:
首先,如果产生一个系统异常,容器就自动回滚事务。
其次,通过调用 EJBContext 接口的 setRollbackOnly 方法,Bean 方法就指示容器回滚事务。如果容器产生一个应用程序异常,回滚就不是自动执行,但可以通过调用 setRollbackOnly 来启动。
容器回滚一个事务时,容器始终撤销事务内 SQL 调用进行对数据的更改。但是,只有在实体 Bean 中时,容器才撤销对实例变量的更改(容器通过自动调用实体 Bean 的 ejbLoad 方法执行此操作,ejbLoad 方法从数据库加载实例变量。)
回滚发生使,会话 Bean 必须明确重设在事务内更改过的任何实例变量。重设会话 Bean 的实例变量的最容易的方法是实施 SessionSynchronization 接口。
同步会话 Bean 的实例变量
SessionSynchronization 接口(在会话 Bean 中为可选)使您可以将实例变量与数据库中的相应值进行同步。容器在事务的各个主要阶段调用 SessionSynchronization 方法 - afterBegin、beforeCompletion 和 afterCompletion。
afterBegin 方法 - 通知实例新的事务已经开始。容器在调用业务方法之前直接调用 afterBegin。afterBegin 方法是一个从数据库加载实例变量的好地方。
beforeCompletion 方法 - 容器在业务方法完成之后而在业务提交之前调用 beforeCompletion 方法。beforeCompletion 方法是会话 Bean(通过调用 setRollbackOnly)回滚事务的最后机会。
如果尚未用实例变量的值更新数据库,会话 Bean 可以在 beforeCompletion 方法中执行此操作。
afterCompletion 方法 - 标识事务已完成。该方法有一个布尔参数。如果提交了事务,该参数的指就是 True(真)。
如果发生回滚,会话 Bean 就可以从 afterCompletion 方法中的数据库刷新其实例变量。
容器管理事务中不能有的方法
对于容器管理事务,不要调用可能干扰容器设置的事务界限的任何方法。禁止调用的方法是:
java.sql.Connection 的 commit, setAutoCommit 和 rollback 方法
avax.ejb.EJBContext 的 getUserTransaction 方法
javax.transaction.UserTransaction 的任意方法
但是,您可以使用这些方法在 Bean 管理事务中设置界限。
使用 Bean 管理事务
在一个 Bean 管理事务中,会话 Bean 或消息驱动型 Bean 中的代码明确标记事务的界限。通过把事务管理移动到 Bean 级,您能够将 Bean 的所有活动 - 甚至那些没有直接连接到数据库访问的活动 - 置于与您的数据库调用的相同事务控制。这保证一个 Bean 控制的所有应用程序部分作为同一事务的部分运行。
在故障情形下,要么提交 Bean 承担的一切,要么回滚一切。
以下各节与开发具有 Bean 管理事务的 Enterprise Bean 相关:
选择事务类型
针对会话 Bean 或消息驱动型 Bean 对某个 Bean 管理事务进行编码时,必须确定是使用 JDBC 还是 JTA 事务。
注意
在具有 Bean 管理事务的会话 Bean 中,可以将 JDBC 和 JTA 事务混合在一起。但是,不提倡采用此做法因为这使得很难调试和维护代码。
以下各节讨论两种事务类型:
JDBC 事务
JDBC 事务受数据库的事务管理器控制。在会话 Bean 内包含原有代码时,可能需要使用 JDBC 事务。
要对某个 JDBC 事务进行编码,您调用 java.sql.Connection 接口的提交和回滚方法。事务开头不明确。事务以遵循最近提交、回滚或连接语句的第一个 SQL 语句开头。(此规则通常是准确的,但可能会随数据库供应商的不同而有所变化。)
有关 JDBC 的其他信息,请参阅 Sun ONE 应用服务器开发者指南中的“J2EE 特性和服务”。
JTA 事务
JTA 允许您在不受事务管理器实施的情况下划分事务。J2EE SDK 利用 JTS 实施事务管理器。但您的代码不直接调用 JTS 方法,而是调用 JTA 方法,JTA 方法然后调用低级 JTS 例程。
JTA 事务受 J2EE 事务管理器控制。您可能需要使用一个 JTA 事务,因为 JTA 事务可以把更新分发到多个来自不同供应商的数据库。某个数据库的事务管理器可能无法与异类数据库工作。
J2EE 事务管理器确实有一个局限性 - 不支持嵌套式事务。换句话说,J2EE 在前一个事务结束之后才能为某个实例启动事务。
有关 JTA 的其他信息,请参阅 Sun ONE 应用服务器开发者指南中的“J2EE 特性和服务”。
在不提交的情况下返回
开始某个业务方法中的事务的具有 Bean 管理事务的无状态会话 Bean 必须先提交或回滚事务,然后才能返回。但是,有状态会话 Bean 则没有此限制。在包含 JTA 事务的有状态会话 Bean 中,多个客户端调用中都保留有 Bean 实例与事务之间的关联。
Bean 管理事务中不允许有的方法
对于 Bean 管理事务,不要调用 EJBContext 接口的 getRollbackOnly 和 setRollbackOnly 方法。请只在容器管理事务中使用这些方法。
注意
对于 Bean 管理事务,调用 UserTransaction 接口的 getStatus 和 rollback 方法。
设置事务超时
对于容器管理事务,通过确定 server.xml 文件中 timeout-in-seconds 属性的值,可以控制事务超时间隔。例如,您会把超时值设置为 5 秒,如下所示:
timeout-in-seconds=5
有了此设置,如果事务没有在 5 秒钟内完成, EJB 容器就回滚事务。
注意
只有使用容器管理事务的 Enterprise Bean 会受到 timeout-in-seconds 属性的影响。对于使用 Bean 管理 JTA 事务的 Enterprise Bean,您调用 UserTransaction 接口的 setTransactionTimeout 方法。
处理隔离级别
事务不仅确保完全完成(或回滚)事务中包含的语句,而且可隔离语句修改的数据。隔离级别描述所更新的数据对于其他事务的可见程度。
如果事务允许其他程序读取未授权的术,可能会提高性能,因为其他程序不必等待事务结束。但是,这也可能导致问题 - 如果事务随后回滚,另一个程序可能会读取错误数据。
对于具有 Bean 管理持久性的实体 Bean,以及对于所有会话 Bean,您可以通过基本数据库提供的 API 以编程方式设置隔离级别。例如,一个数据库使您可以通过调用 setTransactionIsolation 方法来允许未经授权的读取。
对于使用容器管理持久性的实体 Bean,您可以使用 sun-cmp-mapping.xml 文件中的 consistency 元素设置隔离级别。
注意
不要在事务中更改隔离级别。通常情况下,这样的更改会使数据库软件发出不明确的承诺。由于数据库供应商提供的隔离级别可能会发生变化,您应查阅数据库文档,以便了解详细信息。每个针对 J2EE 平台标准化隔离级别。
Copyright 2002 Sun Microsystems, Inc. All rights reserved.
Last Updated October 09, 2002