您现在的位置是:首页 >技术杂谈 >Spring 事务和事务传播机制网站首页技术杂谈

Spring 事务和事务传播机制

kaiwawah 2024-06-17 11:26:35
简介Spring 事务和事务传播机制

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)的概念,嵌套事务进⼊之后相当于新建了⼀个保存点,⽽滚回时只回滚到当前保存点,因此之前的事务是不受影响的

 

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。