mysql传播行为_MYSQL事务的传播行为看这一篇就够了

B、事务的传播行为

1. propagation_required(默认)

需要事务,默认的传播行为,如果当前存在事务,就沿用当前事务

否则新建一个事务运行子方法

即只要大方法里面有事务,小方法即使没有事务,也会用大事务的

说明都在沿用当前存在的事务,

2. propagation_required_new(用于子方法)

无论当前事务是否存在,都会创建新事物运行方法

这样新事务就会拥有新的锁和新的事务隔离级别,与当前事务互相独立

2. propagation_nested(用户子方法)

在当前方法调用子方法时,如果子方法发生异常,并且被捕获的情况下

只回滚子方法执行的SQL,而不回滚当前方法执行的事务

C、 测试传播行为

1. 首先调整Springboot日志级别,调到Debug模式

在yml文件中

logging:

config: classpath:conf/logback-dev.xml

level:

root: debug

2. 在说事务的传播行为的时候,我们先看这个

class B(){

void methodB(){

doSomething..........

}

}

class A(){

void methodA(){

dosomething.....

void methodB(){

dosomething.......

}

}

}

Class B 的 methodB()方法如下:

public MyRedisDO insert(MyRedisDO myRedisDO) {

myRedisMapper.insert(myRedisDO);

return myRedisDO;

}

Class A 的methodA()方法

public int insert(ListmyRedisDOList) {

int count = 0;

MyRedisDO jihe = new MyRedisDO();

jihe.setName("这是集合插入的");

jihe = myRedisService.insert(jihe);

for (MyRedisDO myRedisDO : myRedisDOList) {

MyRedisDO myRedisDO1 = myRedisService.insert(myRedisDO);

if (myRedisDO1 != null) {

count++;

}

}

return count;

}

测试例子:

我想要在批量插入语句里面,插入5条记录,但是第三条出现了问题,插入不进去

@Test

public void co() {

MyRedisDO mapperDO = new MyRedisDO();

mapperDO.setName("2a");

MyRedisDO mapperDO1 = new MyRedisDO();

mapperDO1.setName("2b");

MyRedisDO mapperDO2 = new MyRedisDO();

MyRedisDO mapperDO3 = new MyRedisDO();

mapperDO3.setName("2d");

MyRedisDO mapperDO4 = new MyRedisDO();

mapperDO4.setName("2e");

ListmapperDOList = new ArrayList<>();

mapperDOList.add(mapperDO);

mapperDOList.add(mapperDO1);

mapperDOList.add(mapperDO2);

mapperDOList.add(mapperDO3);

mapperDOList.add(mapperDO4);

int count = myRedisBatchService.insert(mapperDOList);

System.out.println(count);

}

3. 1 情况一:

单个的插入方法和批量方法都不设置事务

结果是:每插入一条,数据库出现一条

最后插入了1+2=3条记录

批量插入第二条之后的都没有插入

因为插入了,所以ID自增了3

3.2 情况二:

单个的insert方法不设置传播行为,集合insertBatch方法设置传播行为为: Propagation.REQUIRED

发现日志中每一个子方法都会有**Participating in existing transaction** 这个

说明都在沿用当前存在的事务,即只要大方法里面有事务,小方法即使没有事务,也会用大事务的

如果ID自增,会出现之前由于异常的数据也算插入进去了,只不过给删掉了,也就是ID自增了

比如原有ID最大是33,一次集合插入前10个都没有问题,此时ID自增到了43,而44出现异常,回滚了所有数据,

这个时候下次插入的时候,会从44开始

结果是:每插入一条即使成功,数据库也不会出现一条

最后插入了0条记录

插入了但是回滚了,但是ID也自增了3

3.3 情况三

单个插入方法时propagation_new级别,大事务是默认Propagation.REQUIRED

则小事务不会管大事务的事务,它自己独立

既然是独立,那我们猜想那前两个数据会不会被插入呢?

结果是:每插入一条,数据库出现一条

最后插入了1+2=3条记录

批量插入第二条之后的都没有插入

因为插入了,所以ID自增了3

3.4 情况四

单个插入方法是propagaion_nested 默认,大的事务是propagaion_required

nested当前方法调用子方法时,如果子方法发生异常,只回滚子方法执行过的SQL

而不回滚当前事务,一般用于大的方法

比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,

ServiceB.methodB的事务级别为PROPAGATION_NESTED,

那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,

ServiceB.methodB会起一个新的子事务并设置savepoint,

等待ServiceB.methodB的事务完成以后,他才继续执行。

因为ServiceB.methodB是外部事务的子事务,那么

1、如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB也将回滚

2、如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA的try..catch捕获并处理

ServiceA.methodA事务仍然可能提交;如果他抛出的异常未被ServiceA.methodA捕获处理,ServiceA.methodA事务将回滚。

3.4.1 第一种情况:不捕获异常

单个插入方法是propagaion_nested 默认,大的事务是propagaion_required

@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)

public int insert(ListmyRedisDOList) {

MyRedisDO my1 = new MyRedisDO();

my1.setName("大方法里面的");

my1 =myRedisService.insert(my1);

int count=0;

for(MyRedisDO myRedisDO:myRedisDOList){

MyRedisDO myRedisDO1 =myRedisService.insert(myRedisDO);

if(myRedisDO1!=null){

count++;

}

}

return count;

}

这样没有捕获异常的,所有执行过的都要回滚的,结果就说数据库没有删除,也没有插入

结果是:每插入一条即使成功,数据库也不会出现一条

最后插入了0条记录

插入了但是回滚了,但是ID也自增了3

3.4.2 第二种情况:捕获异常

单个插入方法是propagaion_nested 默认,大的事务是propagaion_required

@Override

@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)

public int insert(ListmyRedisDOList) {

int count = 0;

int a =myRedisService.delById(1);

try {

for (MyRedisDO myRedisDO : myRedisDOList) {

MyRedisDO myRedisDO1 = myRedisService.insert(myRedisDO);

if (myRedisDO1 != null) {

count++;

}

}

}catch (Exception e){

System.out.println("子方法发生异常,但是不影响当前方法里面的事务");

}

return count;

}

这样是先删除了ID为1的,然后插入2条记录,第三条记录之后的没有插入

只是在插入的时候,数据不是插入一条就显示一条

这样我们就当保证当一个方法是批量执行的时候,比如执行了80%,但是不希望全部回滚,我们就可以这样做

propagaion_nested 和propagation_new的区别

在刚才3.4.2捕获异常的情况下我们发现,除了数据是最后一起出现的,和propagation_new达到的效果很像

但是他们还是有区别的:

NESTED传播行为会沿用当前事务的事务的隔离级别和锁特性

REQUIRES_NEW 则可以拥有自己独立的隔离级别和锁特性

?问题,为什么

既然我们可以在 propagaion_nested里面捕获异常,为什么默认的那种方法就不可以捕获异常呢?

情况五:

批量方法捕获异常,单个方法不设置事务,批量方法 也不设置事务

结果是:

每插入一条出现一条数据,ID自增3

插入了1+2两条记录

情况六:

批量方法捕获异常,单个方法不设置事务,批量方法事务为REQUIRED

结果是:

插入了1+2两条记录

批量里面第二条之后的没有插入

ID自增了3

发现子方法不设置NESTED也能实现相同的结果,那为什么子方法还要设置NESTED呢?

这个问题我们一起来想想

@Transactional自调失效的问题

刚才我们所以的代码都是一个类的方法去调用领用另一个类的代码,但是如果是相同类呢?

把单个的方法和批量的方法弄到一个类里面

@Service

public class MyRedisBatch2ServiceImpl implements MyRedisBatch2Service {

@Autowired

private MyRedisMapper myRedisMapper;

@Autowired

private MyRedisBatch2Service myRedisBatch2Service;

@Override

@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)

public int insertList2(ListmyRedisDOList) {

int count = 0;

MyRedisDO jihe = new MyRedisDO();

jihe.setName("这是集合插入的");

jihe = myRedisBatch2Service.insert2(jihe);

for (MyRedisDO myRedisDO : myRedisDOList) {

MyRedisDO myRedisDO1 = myRedisBatch2Service.insert2(myRedisDO);

if (myRedisDO1 != null) {

count++;

}

}

return count;

}

@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)

public MyRedisDO insert2(MyRedisDO myRedisDO) {

myRedisMapper.insert(myRedisDO);

return myRedisDO;

}

}

结果发现大的方法的事务生效了,但是小方法里面的没有生效,默认给它REQUIRED的事务了,

并没有新起一个REQUIRES_NEW事务

WHY?

因为对于@Transaction本身其实是一种事务的约定,其实现原理是AOP,而AOP的原理是动态代理

在自调的过程中,是类自身的调用,而不是代理对象去调用,那么就不会产生AOP

那有没有放大去解决这个问题呢?

答案是肯定有的,我们可以对类进行改造,可以参考网上的例子,这里不做说明

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程简介: 课程总计41课时,从什么是事务讲起,直到分布式事务解决方案,很的0基础基础与提升系列课程。对于难以理解的知识点,全部用画图+实战的方式讲解。 第一部分:彻底明白事务的四个特性:原子性、一致性、隔离性、持久性,用场景和事例来讲解。 第二部分:实战讲数据库事务的6中并发异常:回滚丢失、覆盖丢失、脏读、幻读、不可重复读、MVCC精讲。 第三部分:彻底搞清楚4种事务隔离级别:READ_UNCOMMITTED 读未提交隔离级别、READ_COMMITTED 读已提交隔离级别、REPEATABLE_READ 可重复度隔离级别、SERIALIZABLE 序列化隔离级别 第四部分:彻底搞清楚MySQL的各种锁:行锁、表锁、共享锁、排它锁、Next-Key锁、间隙锁、X锁、S锁、IS锁、IX锁、死锁、索引与锁、意向锁等。 第五部分:彻底搞清楚Spring事务的7种传播级别的原理和使用:PROPAGATION_REQUIRED、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY、PROPAGATION_REQUIRES_NEW、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER、PROPAGATION_NESTED分布式事务的理论基础:RPC定理、BASE理论、XA协议都是什么,原理是什么,有什么关联关系 第六部分:分布式事务的5种解决方案原理和优缺点:2PC两阶段提交法、3PC三阶段提交法、TCC事务补偿、异步确保策略、最大努力通知策略 第七部分:阿里巴巴分布式事务框架Seata:历经多年双十一,微服务分布式事务框架,用一个Nacos+Spring Cloud+Seta+MySql的微服务项目,实战讲解阿里的分布式事务技术,深入理解和学习Seata的AT模式、TCC模式、SAGA模式。 课程资料: 课程附带配套2个项目源码72页高清PDF课件一份阿里巴巴seata-1.1.0源码一份阿里巴巴seata-server安装包一份
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值