spring的事务传播机制

spring的事务是什么?

事务就是一个或多个关联操作,要么都成功,要么都失败。

其就是通过事务的几个特性保证的:【原子性】、【一致性】、【隔离性】、【持久性】

事务特性

  1. 原子性:一个或多个操作,要么成功,要么失败。
  2. 一致性:数据从一个一致性状态变成另个一一致性状态;
  3. 隔离性:一个操作的事务并不会被其他事务所影响;
  4. 持久性:事务提交完成后,数据是不可被外界所影响的。

先定义两个方法(伪代码)

//将传入参数a存入ATable
pubilc void A(a){
    insertIntoATable(a);    
}
//将传入参数b存入BTable
public void B(b){
    insertIntoBTable(b);
}

事务的传播机制

1、REQUIRED

REQUIRED】: spring事务的默认机制,当前没有事务,则创建一个事务,如果存在事务,则加入到该事务中。

注:后面都是通过伪代码进行分析

@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //调用A入参a1
    testB();    //调用testB
}

@Transactional(propagation = Propagation.REQUIRED)
public void testB(){
    B(b1);  //调用B入参b1
    throw Exception;     //发生异常抛出
    B(b2);  //调用B入参b2
}

testMain和testB方法都声明了required事务,testMain中调用了testB方法,所以testB会加入到testMain的事务中,共享一个事务。
当testB中抛出异常后,B(b1)操作回滚,testMain在同一个事务,所以也需要回滚。
所以a1、b1都不会插入,b2由于在异常后面,所以也不会插入成功。

另外举一个例子:

public void testMain(){
    A(a1);  //调用A入参a1
    testB();    //调用testB
}

@Transactional(propagation = Propagation.REQUIRED)
public void testB(){
    B(b1);  //调用B入参b1
    throw Exception;     //发生异常抛出
    B(b2);  //调用B入参b2
}

a1插入成功,b1和b2都失败
因为testMain没有声明事务,testB声明了required,testB自己会创建一个,其抛出异常,只会回滚自己的,调用方不受影响。所以a1能插入成功。

2、SUPPORTS

SUPPORTS】: 如果当前存在事务,则加入当前事务,如果当前没有事务,则以非事务执行。

public void testMain(){
    A(a1);  //调用A入参a1
    testB();    //调用testB
}

@Transactional(propagation = Propagation.SUPPORTS)
public void testB(){
    B(b1);  //调用B入参b1
    throw Exception;     //发生异常抛出
    B(b2);  //调用B入参b2
}

a1和b1都插入成功,b2失败
由于testMain没有声明事务,testB声明的support,所以testB也没有事务,testB的异常未对testMain产生影响,a1和b1都不用回滚;

但是,如果testMain声明required事务机制,那么testMain和testB都会放到同一个事务中,两个都要回滚。

3、MANDATORY

MANDATORY】:当前存在事务,则加入事务,如果不存在事务,则抛出异常

public void testMain(){
    A(a1);  //调用A入参a1
    testB();    //调用testB
}

@Transactional(propagation = Propagation.MANDATORY)
public void testB(){
    B(b1);  //调用B入参b1
    throw Exception;     //发生异常抛出
    B(b2);  //调用B入参b2
}

a1插入成功,b1和b2失败
testMain没有声明事务,所以testB的异常与否不会影响a1的插入操作;
由于testMain没有声明事务,所以执行testB()的时候会抛出异常,b1的操作都没有执行,所以b1和b2都不会成功。

如果testMain声明required,那么a1和b1都会回滚。

4、REQUIRES_NEW

REQUIRES_NEW】: 无论如何,都要创建一个新的事务,如果当前存在事务,则挂起不管

@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //调用A入参a1
    testB();    //调用testB
    throw Exception;     //发生异常抛出
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testB(){
    B(b1);  //调用B入参b1
    B(b2);  //调用B入参b2
}

a1插入失败,b1和b2成功。
testMain声明的required,本来可以让testB共享其事务,但是testB声明的requires_new,自己单独创建了一个事务,不和testMain搞一起,所以testMain中抛出异常对testB没有丝毫影响。反而testMain自己要回滚。

5、NOT_SUPPORTED

NOT_SUPPORTED】: 无论如何,都不创建事务,即使当前存在事务,也挂起不用

@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //调用A入参a1
    testB();    //调用testB
}

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testB(){
    B(b1);  //调用B入参b1
    throw Exception;     //发生异常抛出
    B(b2);  //调用B入参b2
}

b1插入成功,a1和b2失败。
testMain声明required开启事务,testB抛出异常,所以a1插入回滚;
testB声明not_supported,不开启事务,所以其抛出异常,b1 不用回滚。

6、NEVER

NEVER】: 始终不开启事务,如果当前存在事务,则报错;

@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //调用A入参a1
    testB();    //调用testB
}

@Transactional(propagation = Propagation.NEVER)
public void testB(){
    B(b1);  //调用B入参b1
    B(b2);  //调用B入参b2
}

都不成功;
testMain开启了事务,testB不允许开启事务,所以直接报错,b1和b2都不会执行,然而a1被回滚。

7、NESTED

NESTED】: 如果当前存在事务,则嵌套其中,如果不存在则创建一个事务(类似于required)

和【REQUIRED_NEW】的区别:
REQUIRED_NEW是无论如何都自己开启一个事务,和其他事务没有关系;而nested是父事务开启一个嵌套事务(子事务),如果父事务回滚了,那么子事务也会回滚;

和【REQUIRED】的区别:
REQUIRED是关联操作都放在一个事务里,无论是否catch异常,所有操作都要回滚;而nested是子事务抛出异常回滚了,父事务如果catch住了,那么父事务无需回滚。

@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //调用A入参a1
    testB();    //调用testB
    throw Exception;     //发生异常抛出
}

@Transactional(propagation = Propagation.NESTED)
public void testB(){
    B(b1);  //调用B入参b1
    B(b2);  //调用B入参b2
}

所有操作都回滚了
testMain声明的required,testB与其共享一个事务,所以testMain异常时,父子事务都要回滚。

@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //调用A入参a1
    try{
        testB();    //调用testB
    }catch(Exception e){

    }
    A(a2);
}

@Transactional(propagation = Propagation.NESTED)
public void testB(){
    B(b1);  //调用B入参b1
    throw Exception;     //发生异常抛出
    B(b2);  //调用B入参b2
}

a1和a2插入成功
testB声明的nested,抛出异常,b1回滚,但是testMain中对testB抛出的异常catch了,不影响testMain的事务,所以a1和a2能插入成功。

参考:
来源知乎

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值