Spring 事务和事务传播机制

文章介绍了事务的概念,强调其原子性特征,以及在数据库操作中的重要性。接着,讨论了事务的两种实现方式:手动编程和注解(@Transactional)声明式事务,并详细解释了注解的工作原理。然后,详细阐述了Spring支持的四种事务隔离级别及其防止并发问题的作用。此外,还提到了事务失效的常见场景,如try-catch处理、非public方法、timeout超时等,并给出了相应的解决策略。最后,概述了Spring的事务传播机制,包括REQUIRED、SUPPORTS、REQUIRES_NEW等行为,并通过业务场景举例帮助理解。
摘要由CSDN通过智能技术生成

1.什么是事务(为什么要有事务)

        事务就是将一组操作封装成一个执行单元(封装到一起),要么一起成功,要么一起失败。

在打账的情景上,A向B转账200 元,A的账户-200.B的账号+200,但是如果是一些特殊情况,A的账号-200之后,但是B账号并没有加上这个200元子的话,就会出现很大的问题,而出现这一大问题的原因,就是没有将转账这个业务变成事务。这里也体现了事务的原子性一大特征。

2.事务的实现

        手动、自动的实现俩种方式。手动就是编程的方式(手写代码),另一种自动就是声明式(注解)。

2.1 手动的实现:

        

 

2.2 注解实现

        注解实现极为简单就是在方法前加上个 @Transactional 注解就ok 了,如果方法报错,就将所有已经进行的操作进行回滚,如果没有错误,那就正常执行不进行回滚。

        

@Transactional 可以⽤来修饰⽅法或类:
        修饰⽅法时:需要注意只能应⽤到 public ⽅法上,否则不⽣效。推荐此种⽤法。
        修饰类时:表明该注解对该类中所有的 public ⽅法都⽣效

2.3 @Transactional原理

@Transactional 是基于 AOP 实现的,AOP ⼜是使⽤动态代理实现的。如果⽬标对象实现了接⼝,默认情况下会采⽤ JDK 的动态代理,如果⽬标对象没有实现了接⼝,会使⽤ CGLIB 动态代理。
@Transactional 在开始执⾏业务之前,通过代理先开启事务,在执⾏成功之后再提交事务。如果中途遇到的异常,则回滚事务。
 


常见的参数设置: 

 


        

3.事务的隔离级别

Spring事务管理框架支持数据库的四种隔离级别,可以在@Transactional注解中设置isolation属性来指定所需的隔离级别。具体隔离级别如下:

  1. DEFAULT:使用默认隔离级别,由底层数据源管理器决定。

  2. READ_UNCOMMITTED(读未提交):事务可以读取到其他事务修改但未提交的数据,可能会导致脏读问题的出现。

  3. READ_COMMITTED(读已提交):事务只能读取到其他事务已经提交的数据,可以避免脏读问题,但仍可能会出现不可重复读和幻读问题。

  4. REPEATABLE_READ(可重复读):事务在执行期间可以重复读取相同的记录,并保证读到的是事务开始时的状态。可以避免脏读和不可重复读问题,但仍可能会出现幻读问题。

  5. SERIALIZABLE(串行化):事务需要串行执行,完全避免了并发问题的出现,但同时也影响了数据库的并发性能。

事务的隔离级别是指在并发环境下多个事务之间隔离的程度,主要是为了避免各种并发问题的出现,如脏读、不可重复读和幻读等。


        常见的事务隔离级别有以下四种:

  1. READ UNCOMMITTED(读未提交):事务可以读取到其他事务修改但未提交的数据,可能会导致脏读问题的出现。

  2. READ COMMITTED(读已提交):事务只能读取到其他事务已经提交的数据,可以避免脏读问题,但仍可能会出现不可重复读和幻读问题。

  3. REPEATABLE READ(可重复读):事务在执行期间可以重复读取相同的记录,并保证读到的是事务开始时的状态。可以避免脏读和不可重复读问题,但仍可能会出现幻读问题。

  4. SERIALIZABLE(串行化):事务需要串行执行,完全避免了并发问题的出现,但同时也影响了数据库的并发性能。

下面详细介绍各种隔离级别的特点和应用场景:

  1. READ UNCOMMITTED(读未提交):该隔离级别下,由于事务可以读取到其他事务尚未提交的数据,因此可能会出现脏读问题。该隔离级别通常不应用于生产环境,仅用于特定的性能测试或调试场景。

  2. READ COMMITTED(读已提交):该隔离级别下,事务只能读取到其他事务已经提交的数据,可以避免脏读问题。但由于非锁定读取,依然可能会出现不可重复读和幻读问题。

  3. REPEATABLE READ(可重复读):该隔离级别下,事务在执行期间可以重复读取相同的记录,并保证读到的是事务开始时的状态。可以避免脏读和不可重复读问题,但仍可能会出现幻读问题。MySQL的默认隔离级别即为REPEATABLE READ。

  4. SERIALIZABLE(串行化):该隔离级别下,事务需要串行执行,完全避免了并发问题的出现。但同时也影响了数据库的并发性能,通常应用于对数据安全要求极高的场景,如银行、财务等系统。

● 脏读:⼀个事务读取到了另⼀个事务修改的数据之后,后⼀个事务⼜进⾏了回滚操作,从⽽导致第⼀个事务读取的数据是错误的
● 不可重复读:⼀个事务两次查询得到的结果不同,因为在两次查询中间,有另⼀个事务把数据修改了。
● 幻读:⼀个事务两次查询中得到的结果集不同,因为在两次查询中另⼀个事务有新增了⼀部分数据

 

4. 事务失效的场景

        4.1 try catch

         在try catch 将错误代码包裹之后,catch里面不加任何操作的话,是会出现问题的,出现这个现象的原因是,因为Spring中事务是会自动识别错误代码的,但是你如果加上了try catch的话就说明你是知道这里有错的,Spring就不会帮你识别了,也就不会自动的进行回滚操作了。

 可以看到并没有实现事务回滚操作。

 

        4.1解决方案:

1.你把这个错误抛出不就行了嘛 2.通过代码的方式手动回滚

第一种就是throw就行,第二种方式要讲一下:

        4.2 非public修饰方法

SpringBoot事务失效的场景有哪些? | Javaᶜⁿ 面试突击 (javacn.site)

注解只能在public修饰的方法使用。

        4.3 timeout超时

注解上是可以设置超时时间的,如果设置的时间比较小,而执行代码的时间大于设置的时间,就会导致本来要插入的数据没法正常插入。

        4.4 数据库不支持事务

        4.5 调用类内部的@Transactional方法

 本来是要进行回滚的,但实际情况是报错,但是数据还是存入到数据库中的。

5. Spring事务的传播机制

        Spring事务传播机制是指多个事务在互相调用的情况下,如何管理这些事务的提交和回滚。

Spring 提供了七种事务传播行为,分别是:

  1. REQUIRED(默认传播行为):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  2. SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。
  3. MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  4. REQUIRESNEW:创建一个新的事务,如果当前存在事务,则挂起该事务。
  5. NOTSUPPORTED:以非事务方式执行操作,如果当前存在事务,则挂起该事务。
  6. NEVER:以非事务方式执行操作,如果当前存在事务,则抛出异常。
  7. NESTED:如果当前存在事务,则在嵌套事务中执行;如果当前没有事务,则创建一个新的事务。

著作权归 JavaCN.site 所有 原文链接:https://www.javacn.site/spring/propagation.html

        我们通过支持当前事务和不支持当前事务和嵌套事务分为三类:

5.1 支持当前事务

        REQUIRED

        当前有事务就加入,没有就创建新的。

        SUPPORTS

        有事务就加入,没有就以非事务的方式执行。

        MANDATORY

        有事务就加入,没有就抛异常

5.2 不支持当前事务

        REQUIRESNEW

        创建一个新事务,如果有事务就将这个事务挂起

        NOTSUPPORTED

        以非事务的方式执行,如果有事务就挂机这个事务

        NEVER

        以非事务的方式执行,如果有事务就抛异常

5.3 嵌套事务

        NESTED

        如果当前是有事务就嵌套进这个事务中,如果没有事务就创建一个新的事务。

我们创建一个业务场景来简单理解一下这里的事务传播机制:

        我们先在controller层调用UserController类里面的UserService(service层),在user的service层有俩个操作:一是调用usermapper的add,另一个是调用了log的service层(进行logmapper的add)。

        

 

嵌套事务(NESTED)和加⼊事务(REQUIRED )的区别:

整个事务如果全部执⾏成功,⼆者的结果是⼀样的。
如果事务执⾏到⼀半失败了,那么加⼊事务整个事务会全部回滚;⽽嵌套事务会局部回滚,不会影
响上⼀个⽅法中执⾏的结果.

嵌套事务只所以能够实现部分事务的回滚,是因为事务中有⼀个保存点(savepoint)的概念,嵌套事务进⼊之后相当于新建了⼀个保存点,⽽滚回时只回滚到当前保存点,因此之前的事务是不受影响的

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值