本文参考《java Transaction design strategies》
大部分时候,我们都习惯了spring容器默认的配置,但有时候,我们需要知道更多……
当使用声明式事务模型时,您必须告诉容器如何去管理事务,例如,何时开启一个事务?哪些方法需要事务?当前不存在事务的情况下,容器是否需要为其添加事务控制?事实上,Spring提供了一个bean —— TransactionAttributSource,通过配置其事务(传播)属性(transaction attribute)来达到精确控制事务行为的目的。事务的属性总共有六种:
- Required
- Mandato
- RequiresNew
- Supports
- NotSupported
- Never
Spring又添加了一种新的事务属性,PROPAGATION_NESTED,用于实现真正的嵌套事务,前提条件是外部环境必须提供相应的实现支持。尽管可以通过配置的方式在bean(类)级别指定事务属性,一般来说,还是应该将事务属性应用于方法级别。为整个bean配置某个事务属性意味着其内部所有的方法都采用它,而方法级别的事务属性可以将其覆盖。
REQUIRED
Required属性告诉容器某个特定的方法需要一个事务,如果上下文中已经存在事务,则加入;否则,开启一个事务。这是一种使用最频繁的事务属性,适用于大多数情况;但是,并不绝对,下面我们将会看到对于某些场景,总会有更好的理由去使用Mandatory属性。
MANDATORY
Mandatory属性告诉容器某个特定的方法需要一个事务。但是,不同于Required属性,它无论如何都不会开启新的事务;相反的,它会“强制”要求该方法被调用时上下文中必须存在事务,否则会抛出TransactionRequiredException异常,提示需要一个事务但没有找到。何时选择Mandatory,后面将会专门抽出一节来分析。
REQUIRESNEW
RequiresNew属性告诉容器某个特定的方法需要一个新事务的支持。如果上下文中已经存在事务A,则该事务A挂起,并启动一个新的事务B。当事务B结束后,事务A被唤醒并继续执行。事实上,使用RequiresNew违反了事务的ACID原则,因为新事务会导致原有事务的挂起。
该属性在某个行为必须被完成(提交或回滚)而不受外部事务结果影响时十分有用,例如记录日志。大多数交易系统的每一个操作都必须写进日志,无论其执行结果如何(成功或失败)。假设某个股票交易的方法placement()启动了一个事务,并且调用了一个通用的方法audit()来记录日志。由于audit()和placement()处于同一个事务的管辖范围之内,因此一旦placement()回滚,audit()记录的日志也会相应的进行回滚;这就违背了“任何成功或失败的操作都必须记录日志”这一业务逻辑。这个时候,如果将audit()的事务属性设作RequiresNew,则确保了audit()在一个新的、单独的事务中记录日志,因此不受placement()中外部事务的影响。
SUPPORTS
Supports属性告诉容器,该方法不需要事务支持,但如果当前上下文中已经存在了一个事务,则加入其中。Supports这是一个相当强大、相当有用的事务属性。考虑这样一个场景,业务方法A用来查询某个交易者特定时期的交易总量。由于是查询操作,因此这个时候事务并不是必须的,因此我们将其事务属性设为Supports,来告诉容器在调用方法时不要开启新的事务。但是,如果方法A被某个已经存在事务控制的方法B所调用,那么它就会加入当前事务;那么,在该事务提交之前,方法B中对数据的任何修改对于方法A来说,都是可见的。
下面我们举个更具体的例子来说明Supports的作用。假设某位交易者一天的最大交易额是一百万,如果采用Supports作为事务属性,一次超额交易的具体处理流程如下:
- 目前为止,当天总共的交易量是900,000
- 事务启动
- 交易者又进行了一笔200,000的交易
- 调用事务属性为Supports的查询方法,因为是同一个事务,因此得到结果为1,100,000
- 业务逻辑判断,已经超过最大交易限额,抛出异常,事务回滚
如果使用NotSupported会发生什么呢?我们不会得到任何异常,因为查询方法不会加入当前事务,因此它看不见当前事务中对数据库的任何修改。
- 目前为止,当天总共的交易量是900,000
- 事务启动
- 交易者又进行了一笔200,000的交易
- 调用事务属性为SNotupported的查询方法,因为不属于当前事务,因此得到结果为900,000
- 业务逻辑判断,没有超过最大交易限额,事务提交(实际已超过当天限额)
NotSupported
NotSupported属性告诉容器,该方法不需要事务支持;如果当前上下文中已经存在事务,则该事务被挂起直到该方法执行完毕。如果当前上下文中不存在事务,该方法则在没有事务支持的环境下执行。NotSupported适用于“某些方法在事务控制下有较大可能性会产生异常”的场合。例如在XA架构的事务处理过程中,调用包含DDL语句的存储过程往往会抛出异常,因此比较好的做法是将其设为NotSupported,暂时挂起当前事务。
Never
Never属性告诉容器,该方法必须在无事务的上下文中运行。注意,这与NotSupported不同,后者意味着,如果上下文中存在事务,则将该事务暂时挂起并在无事务的上下文中运行。但Never却不同,如果当前上下文中已经存在事务,则在调用该方法时会抛出一个异常,提示该方法不能在事务环境下运行。因为使用Never会导致各种意料之外的运行时异常,因此除非必要,一般不推荐使用
Required vs. Mandatory
Required和Mandatory是两个往往容易被混淆的事务属性。两者都可以运行在事务上下文环境中,但当上下文中不存在事务时,Required会自己开启一个事务,而Mandatory则不会。大多数情况下,你可以采用如下的“最佳实践”来区分何时需要哪种策略:如果某一方法需要运行在具备事务的环境,但其本身又不负责事务的控制(回滚),则该方法应该标志为Mandatory。可以看出,上述最佳实践是基于事务的所有权而得来的。
Nested vs. RequiresNew vs. Required
另外两个比较容易的事务属性是Spring提供的Nested与RequiresNew。如果当前上下文中存在事务,两者都是启动一个新的事务;如果当前上下文中不存在事务,则类似于PROPAGATION_REQUIRED,两者也都新建一个事务。那么,它们有什么区别呢?
PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等。当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行。
另一方面, PROPAGATION_NESTED 开始一个 "嵌套的" 事务, 它是已经存在事务的一个真正的子事务。嵌套事务开始执行时, 它将取得一个 savepoint。如果这个嵌套事务失败, 我们将回滚到此 savepoint。嵌套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。
总结一下,使用嵌套事务的场景总共有两个,如下图所示:
- 需要事务BC与事务AD一起提交,即:作为事务AD的子事务,事务BC只有在事务AD成功提交时(阶段3成功)才提交。这个需求简单称之为“联合提交”。这一点PROPAGATION_REQUIRED可以做到。
- 需要事务BC的回滚不会影响事务AD的提交。这个需求简单称之为“隔离回滚”。这一点,可以通过设置事务属性为PROPAGATION_REQUIRES_NEW做到。
使用PROPAGATION_REQUIRED满足需求1,但子事务BC的rollback会迫使父事务AD也回滚,不能满足需求2。使用PROPAGATION_REQUIRES_NEW满足需求2,但子事务(严格意义上说,这时不应该称之为子事务)BC是一个全新的事务,父事务(严格意义上说,这时也不应该称之为父事务)AD的成功与否完全不影响BC的提交,不能满足需求1。
同时满足上述两条需求就要用到PROPAGATION_NESTED了。PROPAGATION_NESTED在事务AD执行到B点时,设置了savePoint。当BC事务成功提交时,PROPAGATION_NESTED的行为与PROPAGATION_REQUIRED一样。只有当事务AD在D点成功提交时,事务BC才真正提交; 如果阶段3执行异常,导致事务AD 回滚,事务BC也将一起回滚,从而满足了“联合提交”。 当阶段2执行异常,导致BC事务rollback时,因为设置了savePoint的缘故,AD事务可以选择与BC一起rollback或继续阶段3的执行并保留阶段1的执行结果,从而满足了“隔离回滚”。例如,可以把执行BC事务的方法try-catch起来,在catch中选择是否继续向上抛出异常(是,则回滚到A处;否,则回滚到B处)。而是用PROPAGATION_REQUIRED时,即使try-catch住BC的异常,AD事务也一定会被无条件rollback。
- 大小: 11.5 KB
分享到:
相关推荐
深入浅出ExtJS 源代码深入浅出ExtJS 源代码深入浅出ExtJS 源代码深入浅出ExtJS 源代码深入浅出ExtJS 源代码
《深入浅出程序设计(中文版)》的特别之处在于:我们认为你的时间如此宝贵以至于不应该花费在为新概念伤脑筋上面。《深入浅出程序设计》用最新的认知科学和学习理论打造多感官的学习体验,运用适合大脑工作方式的...
《深入浅出MFC》是一本专为C++程序员深入理解Microsoft Foundation Classes (MFC) 库而编写的经典教程。MFC是微软提供的一套C++类库,用于简化Windows应用程序开发,它将Windows API封装成易于使用的C++类。这本书的...
深入浅出Android「Android」是一个基于 Linux 核心(kernel) 的开放手机平台操作系统。与 Windows Mobile、Symbian 等手机操作系统处在同一级别。 对于设备制造商来说,「Android」是一个免费的平台。「Android」操作...
《深入浅出Oracle EBS之核心功能:Distribution应用模块》一文由黄建华撰写,旨在详尽解析Oracle E-Business Suite(简称Oracle EBS)中Distribution模块的核心功能与最佳技术实践。该文档创建于2005年4月12日,最新...
**深入浅出WPF:全书实例源码详解** Windows Presentation Foundation(WPF),作为.NET Framework的一部分,是Microsoft推出的一种全新的用户界面框架,用于构建Windows桌面应用程序。它提供了丰富的图形层、数据...
深入浅出Oracle EBS之Forms开发初级,
深入浅出密码学 常用加密技术原理及应用 密码学入门首选
深入浅出单元测试深入浅出单元测试深入浅出单元测试
本书《深入浅出Node.js》从多个角度细致深入地探讨了Node.js的内部工作机制和应用实践。 首先,Node.js的核心优势之一是其异步I/O模型。传统的Web应用模型中,对于I/O操作(如读写数据库、文件系统和网络请求等),...
深入浅出hibernate 清晰版
《深入浅出西门子WinCC V6》是《深入浅出西门子自动化产品系列丛书》之一,系统地介绍了SIEMENS HMI/SCADA软件WinCC V6.0的主要功能及其组态方法,是学习WinCC V6.0的有益工具。本书分为基础篇和高级篇两大部分:...
WPF深入浅出电子书,可以下载放到手机里看,非常的方便的。
《深入浅出设计模式》总结了许多系统软件在设计开发过程中的难点,力图将设计模式的实际应用与实现原理有机结合起来,破解软件开发人员在学习设计模式过程不能透彻理解并灵活运用设计模式的难题。 所有章节都是先...
《深入浅出学Spring》这一教学视频为Spring框架的学习提供了详尽且易懂的指导,尤其适合初学者。Spring框架是Java平台上的一个开源框架,它提供了一种全面的基础架构支持,让Java开发者能够开发出更为简洁、高效的...
根据提供的文件信息,我们可以推断出这是一篇关于获取《深入浅出JavaScript》电子书的信息。下面我们将基于这个前提,围绕“深入浅出JavaScript”这一主题进行知识点的展开。 ### 深入浅出JavaScript概览 #### 一...
本书《深入浅出WPF》是刘铁猛所著,目的是为了深入浅出地介绍WPF技术。作者利用丰富的开发经验和对技术的深刻理解,将WPF的复杂概念分解成浅显易懂的知识点。全书分为两个主要部分,第一部分主要介绍WPF开发的基础...
《深入浅出 WPF》 完整目录版是一本非常好动书,并且做了完整目录