您还未登录! 登录 | 注册 | 帮助  

您的位置: 首页 > 软件开发专栏 > 开发技术 > 正文

大白话解读Spring事务传播原理,让你事务管理不再懵逼

发表于:2023-05-06 作者:知其然亦知其所以然 来源:知其然亦知其所以然

大家好,我是小米,一个热爱技术分享的程序员。今天我们来谈一下Spring事务传播。在使用Spring进行数据库操作时,我们经常会遇到需要使用事务的情况,而Spring对事务的管理非常方便,其中就包括事务传播机制。

什么是事务传播

事务传播指的是在一个方法调用另一个方法时,事务应该如何进行传播。在Spring框架中,事务传播有多种策略,它们用于控制不同方法之间事务的关系。在使用Spring事务的时候,我们需要了解这些事务传播机制的特点和使用场景,以便更好地控制事务的传播和管理。

事务的不同分类

Spring事务传播机制根据传播的不同情况,可以分为三类:支持当前事务、不支持当前事务、嵌套事务。

支持当前事务:是指当前方法需要在一个事务内执行,如果当前没有事务,则创建一个新的事务。如果当前存在事务,则沿用当前事务。

不支持当前事务:是指当前方法需要在没有事务的情况下执行,如果当前存在事务,则挂起当前事务,执行当前方法,执行完毕后再恢复原先的事务。

嵌套事务:是指在当前事务中开启一个新的事务,这个新的事务可以看做是当前事务的子事务。如果当前没有事务,则创建一个新的事务。

事务的传播方式

Spring事务的传播方式用的比较多的是以下三种:

  • Required(默认):表示当前方法必须在一个事务内执行,如果当前没有事务,则新开启一个事务,如果当前存在事务,则沿用当前事务。它是Spring事务传播机制的默认选项。这种事务传播机制的使用场景是:多个操作需要在同一事务中进行,例如对订单进行下单和扣款操作。
  • Requires_new:表示当前方法必须在一个新的事务中执行,如果当前存在事务,则挂起当前事务,开启新的事务,执行完毕后再恢复原先的事务。这种事务传播机制的使用场景是:需要将当前事务挂起,执行独立的操作,例如对商品进行库存调整和日志记录。
  • Nested:表示当前方法必须在一个嵌套事务中执行,如果当前没有事务,则新开启一个事务,如果当前存在事务,则在当前事务的基础上创建一个嵌套事务。这种事务传播机制的使用场景是:需要对当前事务进行子事务的操作,例如对订单进行部分退款操作。

需要注意的是,Nested只在当前事务是一个真正的事务时才有效,如果当前事务并不是一个真正的事务(例如使用TransactionDefinition.PROPAGATION_NOT_SUPPORTED或TransactionDefinition.PROPAGATION_NEVER时),Nested和Required的效果是一样的。

除了上述传播方式外,Spring还支持其他的传播方式,例如supports、not_supported、mandatory、never等,它们的作用和含义可以根据具体的业务场景进行选择和使用。

判断内外方法是否在同一事务

在使用Spring事务时,我们需要注意内部方法和外部方法是否在同一个事务中。

  • 如果内部方法和外部方法在同一个事务中,那么当内部方法发生异常时,外部方法也会受到影响,此时需要将异常统一在外层处理。
  • 如果内部方法和外部方法不在同一个事务中,那么内部方法的异常不会影响到外部方法,但是外部方法的异常可能会影响到内部方法。
  • 然而,Nested有一个特殊情况,即当内部方法使用Nested传播机制时,内部方法和外部方法不在同一个事务中,但是内部方法的异常仍然会影响到外部方法。

案例:内外方法在同一个事务

为了更好地理解Spring事务的传播机制,我们可以通过一个简单的电商项目来演示上述三种情况。

在该电商项目中,我们有两个Service,一个是OrderService,一个是GoodsService。OrderService负责生成订单,而GoodsService负责扣减库存。两个Service中都有一个reduceStock方法,用来扣减库存。在扣减库存的同时,我们还需要判断库存是否充足。如果库存不足,我们需要抛出一个RuntimeException。

首先,我们来看一下内部方法和外部方法在同一个事务中的情况。在这种情况下,我们可以使用required传播机制。具体实现如下:

图片

图片

在这个例子中,如果库存不足,会抛出一个RuntimeException,整个事务会回滚。如果扣减库存失败,同样会抛出一个RuntimeException,整个事务也会回滚。如果订单生成成功,整个事务会被提交。

案例:内外方法不在同一个事务

接下来,我们来看一下内部方法和外部方法不在同一个事务中的情况。在这种情况下,我们可以使用requires_new传播机制。具体实现如下:

图片

在这个例子中,我们将reduceStock方法的事务传播机制设置为REQUIRES_NEW。在执行该方法时,Spring会将当前事务挂起,创建一个新的事务来执行reduceStock方法。如果reduceStock方法执行成功,则会提交新的事务。如果reduceStock方法执行失败,则会回滚新的事务,但不会影响当前事务。 

在这个例子中,我们将需要扣减的库存数量加了1。这样在扣减库存时,就会发现库存不足。此时,reduceStock方法会抛出一个RuntimeException,新的事务会回滚,但当前事务不会受到影响。因此,订单生成成功,但库存并没有被扣减。 

嵌套事务

最后,我们来看一下嵌套事务的情况。在这种情况下,我们可以使用nested传播机制。具体实现如下:

在这个例子中,我们将reduceStock方法的事务传播机制设置为NESTED。在执行该方法时,Spring会创建一个嵌套事务来执行reduceStock方法。如果reduceStock方法执行成功,则嵌套事务会提交。如果reduceStock方法执行失败,则嵌套事务会回滚,但不会影响外层事务。

在这个例子中,我们在createOrder方法中调用了reduceStock方法,并将需要扣减的库存数量加了2。这样在扣减库存时,就会发现库存不足。此时,reduceStock方法会抛出一个RuntimeException,嵌套事务会回滚,但当前事务不会受到影响。因此,订单生成失败,库存也没有被扣减。

结论

综上所述,Spring事务传播机制提供了灵活的事务管理方式,可以根据不同的业务场景选择不同的传播机制来控制事务的行为。其中,REQUIRES_NEW和NESTED是比较特殊的传播机制,可以在需要的时候使用。

在使用Spring事务时,需要注意以下几点:

  • 事务传播机制的选择要根据业务场景来确定。
  • 如果内层方法和外层方法在同一个事务中,那么内层方法抛出异常时,异常应该由外层方法来处理。
  • 如果内层方法和外层方法在不同的事务中,那么内层方法抛出异常时,不会影响到外层方法。
  • NESTED传播机制是一种比较特殊的传播机制,需要慎重使用。

在实际开发中,我们需要根据不同的业务场景选择合适的事务传播机制,并且要根据实际情况来处理事务异常,以保证事务的正确执行。