Spring 事务

作者 : admin 本文共3154个字,预计阅读时间需要8分钟 发布时间: 2024-06-5 共3人阅读

Spring 事务

Spring 事务种类

在 Spring 中,有两种事务,编程式事务和声明式事务。

编程式事务

编程式事务管理是一种侵入式的事务管理方式,它要求开发者在代码中显式地编写事务管理的逻辑。这种方式提供了更细粒度的事务控制,但同时也增加了代码的复杂性。

优点

  • 提供了更细粒度的事务控制。

  • 可以在运行时动态决定事务的边界。

实现方式:

使用TransactionTemplatePlatformTransactionManager手动管理事务

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.support.TransactionTemplate;
​
public class OrderService {
​
    @Autowired
    private OrderRepository orderRepository;
​
    @Autowired
    private TransactionTemplate transactionTemplate;
​
    public void placeOrder(Order order) {
        transactionTemplate.execute(status -> {
            // 业务逻辑代码
            orderRepository.save(order);
            // 如果此处发生异常,可以手动调用status.setRollbackOnly()来回滚事务
            return null;
        });
    }
}
声明式事务

声明式事务管理是一种非侵入式的事务管理方式,它允许开发者通过配置来管理事务,而不是在代码中显式编写事务管理的逻辑。这种方式通常与Spring框架结合使用,通过AOP(面向切面编程)实现。

优点

  • 代码与事务管理逻辑分离,使得业务逻辑更加清晰。

  • 易于配置和修改,不需要修改源代码。

  • 支持声明式事务的传播行为和隔离级别。

Spring 推荐通过 @Transactional 注解的方式来实现声明式事务管理。

相比较编程式事务,优点是不需要在业务逻辑代码中掺杂事务管理的代码,这就是为什么说它的代码域事务管理逻辑分离(优点的第一条),但是缺点就是最细粒度只能到方法级别,到不了代码块级别。

Spring 的事务隔离级别

SQL 标准定义了四个隔离级别,Spring 都支持,并且提供了对应的机制来配置它们,定义在 TransactionDefinition 接口中。

①、ISOLATION_DEFAULT:使用数据库默认的隔离级别(你们爱咋咋滴 😁),MySQL 默认的是可重复读,Oracle 默认的读已提交。

②、ISOLATION_READ_UNCOMMITTED:读未提交,允许事务读取未被其他事务提交的更改。这是隔离级别最低的设置,可能会导致“脏读”问题。

③、ISOLATION_READ_COMMITTED:读已提交,确保事务只能读取已经被其他事务提交的更改。这可以防止“脏读”,但仍然可能发生“不可重复读”和“幻读”问题。

④、ISOLATION_REPEATABLE_READ:可重复读,确保事务可以多次从一个字段中读取相同的值,即在这个事务内,其他事务无法更改这个字段,从而避免了“不可重复读”,但仍可能发生“幻读”问题。

⑤、ISOLATION_SERIALIZABLE:串行化,这是最高的隔离级别,它完全隔离了事务,确保事务序列化执行,以此来避免“脏读”、“不可重复读”和“幻读”问题,但性能影响也最大。

Spring 事务传播

概念

事务传播是在A事务调用了B事务,此时B事务传播到了A事务中,这样就产生了事务传播。

如果两个方法都存在事务,那么事务传播的 sql 可能如下:

但是这样肯定是会出现问题的。当运行到第二个事务的 begin 的时候,A事务(外层的事务) 会自动提交,那么就会导致A事务失效。

所以为了防止事务失效,我们需要做一些操作Spring 事务插图,可以分为以下的场景。

传播场景

使A具有事务

  1. 既然运行到第二个事务的 begin 的时候,A事务(外层的事务) 会自动提交,那就把 begin 和 commit 去掉,将 B 融入 A 事务中,这样就会使 A 事务失效。

  2. 当执行到 B 事务的时候,将 A 事务挂起,此时触发个连接,B 开启新事务。挂起也就是让 A 事务堵塞,先让B 事务执行,等到B事务结束,在让A 事务执行。

使A 不具有事务

这个简单一点,就是一定要让A没有事务,那么就分成B有事务和没有事务。Spring 事务插图(1)

嵌套事务

嵌套事务意思就是将B还是放在A事务内,但是为了防止A事务失效,我们设置保存点和回滚至保存点,来实现类似两个事务的操作。Spring 事务插图(2)

如果内部的B事务出现问题,并不会影响A事务的提交,只会回滚至保存点。

传播行为
  • REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。Spring 的默认传播行为。

  • SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。

  • MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

  • REQUIRES_NEW:总是启动一个新的事务,如果当前存在事务,则将当前事务挂起。

  • NOT_SUPPORTED:总是以非事务方式执行,如果当前存在事务,则将当前事务挂起。

  • NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前事务不存在,则行为与 REQUIRED 一样。嵌套事务是一个子事务,它依赖于父事务。父事务失败时,会回滚子事务所做的所有操作。但子事务异常不一定会导致父事务的回滚。

这些传播行为都是基于传播场景,理解传播场景就能理解行为。

声明式事务的实现原理

代理和AOP

  1. 通过代理来创建一个代理对象。

    在初始化 Bean 的时候,会进行Bean的后置处理,后置处理时会遍历所有的切面,并查找与当前Bean相匹配的切面,在这里我们要获取事务的属性,有了属性之后才知道是否与切面匹配,这个属性也就是也就是 @Transactional 注解及其属性值。

  2. 代理对象执行目标方法进行事务增强

    声明式事务是一种环绕增强,对应接口为MethodInterceptor

声明式事务失效场景

@Transactional 应用在非 public 修饰的方法上

@Transactional 注解只对公共方法有效。如果注解标注在非公共方法(如私有方法、包级私有方法、受保护的方法)上,事务将不会被应用。

自调用问题

如果方法 B 有事务注解标识,但是同一个类中的方法 A 没有,A中的方法调用方法 B,此时外部调用方法A,方法B的事务会失效。

传播行为设置不当

TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行;错误使用场景:在业务逻辑必须运行在事务环境下以确保数据一致性的情况下使用 SUPPORTS。

回滚设置错误

@Transactional 注解的 rollbackFor 属性用于指定哪些异常类型会导致事务回滚。默认情况下,Spring 事务管理器只在遇到未捕获的 RuntimeExceptionError 时才会触发事务回滚。如果应用程序抛出了其他类型的异常,而没有在 rollbackFor 属性中指定,那么事务将不会回滚,这可能会导致数据不一致或其他问题。

例如,如果一个方法可能会抛出 IOException,但 @Transactional 注解没有设置 rollbackFor 属性,或者设置为 rollbackFor = RuntimeException.class,那么当 IOException 被抛出时,事务将不会回滚,因为 IOException 不是 RuntimeException 的子类。

本站无任何商业行为
个人在线分享 » Spring 事务
E-->