日期:2014-05-16  浏览次数:20526 次

事务属性
除了回滚指令之外,您还必须指定事务属性,这将定义事务的行为。Java 平台支持六种事务属性,而与您使用的是 EJB 还是 Spring Framework 无关:
Required
Mandatory
RequiresNew
Supports
NotSupported
Never
在描述这些事务属性时,我将使用一个假想的 methodA() 方法,事务属性将应用于这个方法。

如果为 methodA() 指定了 Required 事务属性,并且在已有事务作用域内调用了 methodA(),那么将使用已有的事务作用域。否则,methodA() 将启动一个新的事务。如果事务是由 methodA() 启动的,则它也必须由 methodA() 来终止(提交或回滚)。这是最常用的事务属性,并且是 EJB 3.0 和 Spring 的默认属性。遗憾的是,它的使用并不正确,从而造成了数据完整性和一致性问题。对于本系列后续文章将要讨论的各事务策略,我将更加详细地阐述这个事务属性。

如果为 methodA() 指定了 Mandatory 事务属性,并且在已有事务作用域内调用了 methodA(),那么将使用已有的事务作用域。但是,如果调用 methodA() 时没有事务上下文,则会抛出一个 TransactionRequiredException,指示必须在调用 methodA() 之前呈现事务。这个事务属性应用于本文下一部分将要讨论的 Client Orchestration 事务策略。
RequiresNew 事务属性非常有趣。许多时候,我会发现这个属性被误用或误解。methodA() 指定了 RequiresNew 属性,并且在有或没有事务上下文的情况下调用了 methodA(),那么新事务将始终由 methodA() 启动(和终止)。这意味着,如果在另一个事务(比如说 Transaction1)的上下文中调用了 methodA(),那么 Transaction1 将暂停,同时会启动一个新的事务(Transaction2)。当 methodA() 结束时,Transaction2 将提交或回滚,并且 Transaction1 将恢复。这显然违背了事务的 ACID(atomicity、consistency、isolation 和 durability)属性(特别是 atomicity 属性)。换句话说,所有数据库更新都不再包含在一个单一的工作单元中。如果 Transaction1 将进行回滚,则由 Transaction2 提交的更改将仍然被提交。如果是这种情况,那么事务属性有什么好处呢?如本系列第一篇文章所述,这个事务属性仅用于独立于底层事务的数据库操作(比如审计或记录,在本例中为 Transaction1)。

Supports 事务属性也是我发现大多数开发人员未完全理解或掌握的一个地方。如果为 methodA() 指定了 Supports 事务属性,并且在已有事务的作用域中调用了 methodA(),则 methodA() 将在该事务的作用域中执行。但是,如果在没有事务上下文的情况下调用了 methodA(),则不会启动任何事务。此属性主要用于针对数据库的只读操作。如果是这种情况,为何不指定 NotSupported 事务属性(在下一段中讨论)呢?毕竟,该属性将确保方法在没有事务的情况下运行。答案非常简单。在已有事务的上下文中调用查询操作会导致从数据库事务日志中读取数据(换句话说,已更新的数据),而不在事务作用域中运行会导致查询从表中读取未修改的数据。举例来说,如果您插入一个新的交易订单到 TRADE 表中,然后(在相同的事务中)获取所有交易订单的列表,则未提交的交易将出现在列表中。但是,如果您使用的是 NotSupported 事务属性,那么它会造成数据库查询从表中而不是从事务日志中读取数据。因此,在上一个示例中,您将不会看到未提交的交易。这并不一定是一件坏事;这将由您的用例和业务逻辑决定。

NotSupported 事务属性指定被调用的方法将不使用或启动事务,无论是否呈现了事务。如果为 methodA() 指定了 NotSupported 事务属性,并且在某个事务的上下文中调用了 methodA(),则该事务将暂停直到 methodA() 结束。当 methodA() 结束时,原始事务将被恢复。这个事务属性只有少许用例,并且它们主要涉及数据库存储过程。如果您尝试在已有事务上下文的作用域中调用数据库存储过程,并且数据库存储过程包含一个 BEGIN TRANS,或者对于 Sybase 的情况,在未连接模式中运行,则会抛出一个异常,指示已经存在一个事务无法启动新事务。(换句话说,内嵌事务不受支持)。几乎所有的容器都使用 Java Transaction Service (JTS) 作为默认的 JTA 事务实现。不支持内嵌事务的是 JTS 而不是 Java 平台。如果您无法修改数据库存储过程,那么您可以使用 NotSupported 属性来暂停已有事务上下文,以避免这种致命的异常。但是,其影响是您不能在相同的 LUW 中对数据库执行原子更新。这是一种权衡,但有时可以让您迅速脱离困境。

Never 或许是最有趣的事务属性。它的行为类似于 NotSupported 事务属性,但存在一个重要差异:如果使用 Never 事务属性调用方法时已经存在某个事务上下文,则会抛出一个异常,指示您在调用该方法时不允许使用这个事务。我能想到的针对此事务属性的唯一一个用例就是用于测试。它提供了一种简单的方法,用于验证当您调用特定的方法时某个事务是否存在。如果您在调用相应的方法时使用了 Never 事务属性并且接收到了一个异常,则知道事务已经呈现。如果允许执行方法,则您知道事务未被呈现。这是确保事务策略坚固的理想方法。