spring事务的7种传播方式

本文通过具体的代码示例,详细解析了Spring框架中事务的传播机制。分别介绍了Propagation.REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NEVER、NOT_SUPPORTED及NESTED等不同传播行为对业务代码的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代码准备

service层两个insert方法,insert1()和insert2(),insert2()插入两条数据,成功插入第一条后抛出异常

public void insert1(){
    Actor actor=new Actor();
    actor.setActorName("王五");
    actorMapper.insertOrUpdate(actor);
}
public void insert2(){
    Actor actor1=new Actor();
    actor1.setActorName("张三");
    Actor actor2=new Actor();
    actor2.setActorName("李四");
    int result1 = actorMapper.insertOrUpdate(actor1);
    if(result1!=0){
        throw new RuntimeException();
    }
    int result2 = actorMapper.insertOrUpdate(actor2);
}

controller层调用上述两个insert方法

@GetMapping("testInsert")
public void testInsert(){
    actorService.insert1();
    actorService.insert2();
}
  • 在不加任何事物的情况加,insert2()方法在抛出异常后会停止执行,插入结果为王五和张三插入成功,李四插入失败
1、Propagation.REQUIRED:支持当前事务,如果不存在,就新建一个事务(spring默认的事务传播方式)
  • 只在insert2()上加上注解
@Transactional(propagation = Propagation.REQUIRED)
public void insert2(){。。。}

此时执行testInsert()后由于只有insert2()上有注解支持并且只支持当前事务,只有王五插入成功,insert2()中的插入回滚

  • 只在testInsert()上加注解
@Transactional(propagation = Propagation.REQUIRED)
public void testInsert(){。。。}

此时在testInsert()的事务中insert2()和insert1()都不存在事务,所以会创建事务,在insert2(0报错后,事务整体回滚,全部插入失败。

2、Propagation.SUPPORTS: 当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行
  • 只在insert2()上加入事务
@Transactional(propagation = Propagation.SUPPORTS)
public void insert2(){。。。}

此时由于testInset()上没有事务,此时insert2()以非事务方式执行,此时李四无法插入,张三和王五插入成功

3、Propagation.MANDATORY:当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常。
  • 只在insert2()上加注解

    @Transactional(propagation = Propagation.MANDATORY)
    public void insert2(){。。。}
    

    此时只有王五插入成功,张三和李四插入失败的原因并不是事务回滚,而是insert2()方法没有执行,直接抛出的异常

4、REQUIRES_NEW:创建一个新事务,如果存在当前事务,则当前事务挂起
  • 将异常抛出的位置改到testInsert()中去,去掉insert2()中的异常

    public void insert1(){
        Actor actor=new Actor();
        actor.setActorName("王五");
        actorMapper.insertOrUpdate(actor);
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void insert2(){
        Actor actor1=new Actor();
        actor1.setActorName("张三");
        Actor actor2=new Actor();
        actor2.setActorName("李四");
        int result1 = actorMapper.insertOrUpdate(actor1);
        int result2 = actorMapper.insertOrUpdate(actor2);
    }
    
    @GetMapping("testInsert")
    @Transactional(propagation = Propagation.REQUIRED)
    public HttpResult testInsert(){
        actorService.insert1();
        actorService.insert2();
        throw new RuntimeException("");
    }
    

    此时执行testInsert()方法,会发现王五插入失败,张三和李四插入成功,在insert2()上的注解 Propagation.REQUIRES_NEW在加入testInsert()时会重新创建一个单独的事务,而不受testInsert事务的影响,抛出异常导致insert1()回滚。

5、Propagation.NEVER:不使用事务,如果当前存在事务则报错抛出异常
@GetMapping("testInsert")
@Transactional(propagation = Propagation.REQUIRED)
public HttpResult testInsert(){
    actorService.insert1();
    actorService.insert2();
    return HttpResult.ok();
}
public void insert1(){
    Actor actor=new Actor();
    actor.setActorName("王五");
    actorMapper.insertOrUpdate(actor);
}
@Transactional(propagation = Propagation.NEVER)
public void insert2(){
    Actor actor1=new Actor();
    actor1.setActorName("张三");
    Actor actor2=new Actor();
    actor2.setActorName("李四");
    int result1 = actorMapper.insertOrUpdate(actor1);
    int result2 = actorMapper.insertOrUpdate(actor2);
}

此时全部插入失败,原因testInsert()上生命有事务,并且传播方式为REQUIRED,二insert2()上面的事务传播方式为不使用事务,此时insert2()抛出异常不执行方法,而testInsert()是在事务中,捕捉到insert2()的异常后进行回滚。所有插入都不成功。

6、Propagation.NOT_SUPPORTED:始终以非事务方式执行,如果当前存在事务,则挂起当前事务
  • 与Propagation.NEVER的栗子相似,只把insert2()的事务注解改为@Transactional(propagation = Propagation.NOT_SUPPORTED)

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
        public void insert2(){。。。}
    

    此时insert2()以非事务执行,张三插入成功,但是insert2()抛出异常,会使testInsert()发生回滚,insert1()发生回滚

7、Propagation.NESTED:如果当前事务存在,则在嵌套事务中执行,否则REQUIRED的操作一样(开启一个事务)
  • 和REQUIRES_NEW的区别

    REQUIRES_NEW是新建一个事务并且新开启的这个事务与原有事务无关,而NESTED则是当前存在事务时(我们把当前事务称之为父事务)会开启一个嵌套事务(称之为一个子事务)。
    在NESTED情况下父事务回滚时,子事务也会回滚,而在REQUIRES_NEW情况下,原有事务回滚,不会影响新开启的事务。

  • 和REQUIRED的区别

    REQUIRED情况下,调用方存在事务时,则被调用方和调用方使用同一事务,那么被调用方出现异常时,由于共用一个事务,所以无论调用方是否catch其异常,事务都会回滚
    而在NESTED情况下,被调用方发生异常时,调用方可以catch其异常,这样只有子事务回滚,父事务不受影响

带你读懂Spring 事务——事务的传播机制 - 知乎 (zhihu.com)

@GetMapping("testInsert")
@Transactional(propagation = Propagation.REQUIRED)
public HttpResult testInsert(){
    actorService.insert1();
    try {
        actorService.insert2();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return HttpResult.ok();
}
public void insert1(){
    Actor actor=new Actor();
    actor.setActorName("王五");
    actorMapper.insertOrUpdate(actor);
}
@Transactional(propagation = Propagation.NESTED)
public void insert2(){
    Actor actor1=new Actor();
    actor1.setActorName("张三");
    Actor actor2=new Actor();
    actor2.setActorName("李四");
    int result1 = actorMapper.insertOrUpdate(actor1);
    if(result1==1){
        throw new RuntimeException();
    }
    int result2 = actorMapper.insertOrUpdate(actor2);
}

在对insert2()进行异常捕获后,由于insert2上的注解会将其加入父事务,但是由于抛出异常,只有insert2()回滚。只有王五插入成功

  • 若此时在testInsert()中抛出异常

    @GetMapping("testInsert")
    @Transactional(propagation = Propagation.REQUIRED)
    public HttpResult testInsert(){
        actorService.insert1();
        try {
            actorService.insert2();
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new RuntimeException();
    }
    

    此时事务全部回滚,所有插入都失效,insert2()受到父事务的影响也发生回滚(不论在父事务中有没有捕获到insert2()的异常,insert2()都会发生回滚)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个小白QAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值