Spring 高手之路 24——事务类型及传播行为实战指南
1. 编程式事务(不推荐)
定义:编程式事务是指通过显式的编程代码来管理事务的开始、提交和回滚。开发者需要手动控制事务的每个步骤。
优点:
更加灵活:开发者可以根据具体的业务逻辑细节对事务进行精细控制。
适用于需要精细控制的事务逻辑:当事务行为需要根据特定条件进行复杂控制时,编程式事务更为合适。
缺点:
代码冗长:需要手动编写大量的事务管理代码,增加了代码复杂性。
易出错:手动管理事务容易导致漏写提交或回滚的代码,增加了发生错误的风险。
示例(以Spring
的编程式事务为例):
这段代码展示了如何使用Spring
进行编程式事务管理。executeInTransaction
方法中,首先创建了一个事务定义和事务状态。然后,在try
代码块中执行业务逻辑,并在成功时提交事务。如果发生异常,则回滚事务。通过这种方式,可以精细控制事务的开始、提交和回滚,适用于需要复杂事务控制的场景。
2. 声明式事务(推荐)
定义:声明式事务是通过配置或注解的方式来管理事务。开发者无需手动编写事务管理代码,事务的控制交由框架处理。
优点:
简洁明了:使用注解或配置文件即可实现事务管理,代码更加简洁。
降低出错率:自动管理事务,减少手动管理带来的错误。
缺点:
灵活性较差:声明式事务在面对非常复杂的事务逻辑时,可能不够灵活,但可以通过结合使用不同的事务传播行为来部分解决这个问题。
示例(以Spring
的声明式事务为例):
在Spring
中,通常推荐使用声明式事务,因为它更加简洁并且可以充分利用Spring
框架的事务管理功能。在需要复杂事务控制时,可以考虑使用编程式事务。
3. 事务的传播行为(复杂混合事务场景及时序图说明)
REQUIRED:如果当前没有事务,则创建一个新的事务;如果已经存在一个事务,则加入当前事务。
REQUIRES_NEW:每次都创建一个新的事务,如果当前已经有一个事务,则挂起当前事务。挂起当前事务的意思是,当前事务的执行暂停,待新事务完成后再恢复执行。新事务提交后,主事务回滚不会影响这个新事务。
NESTED:如果当前有事务,则在当前事务中嵌套一个事务,若外部事务回滚,则嵌套事务也会回滚;如果当前没有事务,则创建一个新的事务。
MANDATORY:必须在一个现有事务中运行,如果当前没有事务,则抛出异常。
NEVER:不能在事务中运行,如果当前有事务,则抛出异常。
NOT_SUPPORTED:当前方法不支持事务,如果当前有事务,则将事务挂起。
SUPPORTS:如果当前有事务,则加入该事务;如果当前没有事务,也可以非事务方式运行。
对于需要部分事务回滚的复杂场景,Spring
中的声明式事务确实可以通过传播行为来实现一定的灵活控制。以下是一个详细的示例,展示如何使用常见的传播行为来实现部分事务回滚。
复杂混合事务场景示例:
假设有一个订单处理系统,包含以下步骤:
创建订单
扣减库存
发送确认邮件
我们希望在处理过程中:
如果创建订单失败,整个事务回滚。
如果扣减库存失败,只回滚扣减库存这部分,不影响订单创建。
发送确认邮件可以在一个独立的事务中进行,即使失败也不影响前面的步骤。
代码如下:
库存服务InventoryService
邮件服务EmailService
解释
创建订单:使用
REQUIRED
传播行为,作为主事务。如果失败,会抛出异常,导致整个事务回滚。扣减库存:使用
NESTED
传播行为,在主事务内创建一个嵌套事务。如果扣减库存失败,只会回滚这个嵌套事务,不会影响到主事务。发送确认邮件:使用
REQUIRES_NEW
传播行为,总是创建一个新的事务。即使发送邮件失败,也不会影响前面的事务。
通过结合使用事务不同的传播行为,可以在声明式事务中实现复杂的事务管理需求,如部分事务回滚。
整个事务处理过程时序图如下:
时序图解释
客户端调用
OrderService
的processOrder
方法开始主事务。在
OrderService
中创建订单,使用REQUIRED
传播行为。如果订单创建成功,调用
InventoryService
扣减库存,使用NESTED
传播行为。如果库存扣减失败,只回滚嵌套事务,不影响主事务。无论库存扣减是否成功,调用
EmailService
发送确认邮件,使用REQUIRES_NEW
传播行为。即使邮件发送失败,也不影响主事务。如果订单创建失败,回滚主事务,并且所有嵌套事务也会回滚,但独立的新事务不会受到影响。
3.1 NESTED 和 REQUIRES_NEW 传播行为的区别
有人可能疑惑了,这里NESTED
和REQUIRES_NEW
的传播行为看起来很像,这里详细对比一下。
事务关系:
REQUIRES_NEW:
创建一个独立的新事务,与外部事务无关。
如果当前有事务,暂停当前事务,启动一个新事务。
新事务提交或回滚后,外部事务继续。
NESTED:
在当前事务内创建一个嵌套事务。
嵌套事务依赖于外部事务,嵌套事务提交必须等待外部事务提交。
如果当前没有事务,则行为与
REQUIRED
相同,创建一个新事务。
回滚和提交行为
REQUIRES_NEW:
回滚行为:如果新事务失败,只回滚新事务,不影响外部事务。
提交行为:新事务提交后,外部事务继续。即使外部事务回滚,新事务的提交也不会受到影响。
NESTED:
回滚行为:如果嵌套事务失败,只回滚嵌套事务,不影响外部事务。
提交行为:嵌套事务在外部事务提交时才真正提交。如果外部事务回滚,嵌套事务也会回滚。
使用场景
REQUIRES_NEW:
适用于需要独立事务的场景,例如记录日志或发送通知,这些操作应独立完成,不受主事务影响。
NESTED:
适用于部分独立处理但依赖于外部事务的场景,例如部分业务操作需要独立回滚,但整体上需要保持一致性。
欢迎一键三连~
有问题请留言,大家一起探讨学习
----------------------Talk is cheap, show me the code-----------------------
版权声明: 本文为 InfoQ 作者【砖业洋__】的原创文章。
原文链接:【http://xie.infoq.cn/article/c00bfbb2bcca4bdf769a9b302】。
本文遵守【CC BY-NC】协议,转载请保留原文出处及本版权声明。
评论