Spring事务传播行为控制

事务的传播行为

一、概念

  1. 事务的传播是指,两个包含数据库操作的方法间,存在调用和被调用关系时,父子方法间的事务相互关系.
  • 父方法的事务称为当前事务
  • 子方法的事务称为子事务
  1. 以Spring注解式事务为例,使用打在子方法上的@Transactional注解来控制这种传播关系。
    注意:在一个时刻,一个Conection上只能存在一个事务。

二、支持7种行为(都是针对子方法的)

  1. @Transactional(propagation=Propagation.REQUIRED) //子方法需要被托管给事务:若存在当前事务, 则子方法沿用当前事务,即加入当前事务;否则Spring为子方法新建一个子事务(这是默认的模式)【适用于:insert、update、delete】
  2. @Transactional(propagation=Propagation.SUPPORTS) //子方法支持被托管给事务:若存在当前事务, 则子方法沿用当前事务,否则子方法以无事务方式执行【适用于:select】
  3. @Transactional(propagation=Propagation.MANDATORY) //子方法必须在当前事务中执行:若存在当前事务则沿用当前事务;否则抛出异常,而不会新建事务
  4. @Transactional(propagation=Propagation.REQUIRES_NEW) //子方法需要在新事务中执行:无论是否存在当前事务,都为子方法创建一个子事务;并挂起当前事务,待新的子事务执行完毕,再继续执行当前事务;当前事务相互独立,具有独立的隔离级别和数据库锁;父子方法的异常互不影响,父方法发生异常只会回滚父方法的逻辑,子方法照常提交;即使子方法抛出异常,父方法捕获了该异常,此异常也只会引起子方法的回滚,父方法由于只受当前事务控制,故父方法不会回滚。
  5. @Transactional(propagation=Propagation.NOT_SUPPORTED) //子方法不支持事务:若不存在当前事务,父子方法顺序执行;若存在当前事务,当前事务会被挂起,待子方法执行完毕当前事务才继续执行。若当前事务需要回滚,子方法不会参与回滚。
  6. @Transactional(propagation=Propagation.NEVER) //子方法不可以在事务中执行:若存在当前事务则抛出异常(正好与Propagation.MANDATORY相反)
  7. @Transactional(propagation=Propagation.NESTED) //子方法被嵌套到当前事务:子方法沿用当前事务,但当前事务在子方法处设置了savePoint,这样子方法发生回滚时只能恢复到达savePoint位置,而不会导致当前事务发生回滚。但,当前事务若发生回滚却会导致子方法的逻辑回滚。

其中:REQUIRED、REQUIRES_NEW、NESTED在业务场景中用的最多,REQUIRED是Spring默认的行为,后两者则提供了粒度控制上的灵活性。

三、样例

@Component
public class Outer {

      @Resource
      privatee  Inner inner;     
 
     void out() {
         this.inner.in();
     }
}

@Component
public class Inner {
     @Transactional(RollBackFor="Exception.class", timeout = 1000L, propagation=Propagation.NOT_SUPPORTED, readOnly=false, Isolation=Isolation.DEFAULT)
     void in() {
     }      
}

补充

  1. 只读事务:当一个汇总操作中涉及多步查询时,为了保证几步查询间的数据一致性不被并发的其他写操作破坏,这次汇总操作就需要托管到一个只读事务中。只读事务会被数据库做特殊优化,性能高于写事务。同时,只读事务之外不能被写型事务包裹,否则会报错。

  2. 事务超时:表示事务的最大等待秒数,超过则抛异常。默认值为-1,表示使用底层数据库的超时值。

  3. Spring事务是通过AOP机制,用动态代理方式将事务控制逻辑织入到核心逻辑前后的,要想事务逻辑生效,事务方法必须经过织入;亦即事务方法所在的对象必须从Spring的IOC容器中获取,不能发生Bean对象内方法的直接调用。实在需要Bean内方法互调用,可以通过实现ApplicationContextAware接口,在Bean内获得ApplicationContext,然后重新从ApplicationContext获取代理Bean,然后再调用代理对象的方法

posted @ 2020-09-05 22:03  JaxYoun  阅读(379)  评论(0编辑  收藏  举报