tulip notes
首页
  • 学习笔记

    • 《Vue》
  • 踩坑日记

    • JavaScript
  • MQ
  • Nginx
  • IdentityServer
  • Redis
  • Linux
  • Java
  • SpringBoot
  • SpringCloud
  • MySql
  • docker
  • 算法与设计模式
  • 踩坑与提升
  • Git
  • GitHub技巧
  • Mac
  • 网络
  • 项目构建合集
  • 一些技巧
  • 面试
  • 一些杂货
  • 友情链接
  • 项目发布
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Star-Lord

希望一天成为大师的学徒
首页
  • 学习笔记

    • 《Vue》
  • 踩坑日记

    • JavaScript
  • MQ
  • Nginx
  • IdentityServer
  • Redis
  • Linux
  • Java
  • SpringBoot
  • SpringCloud
  • MySql
  • docker
  • 算法与设计模式
  • 踩坑与提升
  • Git
  • GitHub技巧
  • Mac
  • 网络
  • 项目构建合集
  • 一些技巧
  • 面试
  • 一些杂货
  • 友情链接
  • 项目发布
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 探索SpringBoot

    • 初步认识SpringBoot
    • 自定义starter
    • 一些常用注解
    • 自动装配
    • spring中开启事务
    • Spring中事务的属性
      • 隔离性
        • 异常的种类
        • 脏读
        • 不可重复读
        • 幻读
        • 隔离级别
        • SpringBoot中的隔离级别
      • 事务的传播性
        • 7种传播种类
        • 传播属性设置
      • 回滚规则
      • 超时时间
      • 只读属性
      • 事务失效的情况
    • SpringBoot启动流程
    • Spring中Bean的创建跟循环依赖
    • IOC的实现机制是什么
  • 常用功能实现

  • Security认证授权

  • 扩展

  • 实战与注意事项

  • 其它

  • 《SpringBoot》笔记
  • 探索SpringBoot
EffectTang
2023-10-02
目录

Spring中事务的属性

# Spring中事务的属性

上文中说了在SpringBoot中如何开启事务,接下来就说下事务的一些属性

# 隔离性

事务的隔离性:指在一个事务执行期间,对数据库的其他事务进行访问的方式。

在实际开发与应用中存在多个事务同时运行的情况,多个事务同时执行期间对数据的读取可能会出现异常,数据的不一致性。为了解决这些异常因此需要对事务的隔离性进行设置。

事务的隔离性可以保证在一个事务执行期间,其他事务不会看到事务的未提交的更改,也不会影响其他事务的执行。

# 异常的种类

多个事务运行可能导致以下几种异常:

  • 脏读(Dirty Read)
  • 不可重复读(Non-repeatable Read)
  • 幻读(Phantom Read)。

# 脏读

脏读是指在一个事务中读取了另一个事务的未提交的更改,然后在这个事务中对这些更改进行了修改,最后另一个事务提交了这些更改,导致数据的一致性被破坏。

# 不可重复读

不可重复读是指在一个事务中多次读取同一数据,但是读取到的数据不同,这是因为另一个事务在当前事务执行期间修改了这个数据。

假设有一个订单表,其中包含一个customer_id字段,用于存储订单的客户ID。在一个事务中,你读取了一个订单,并记录下了这个订单的customer_id。然后,在另一个事务中,你修改了这个订单的customer_id。最后,在第一个事务中,你再次读取这个订单,那么你可能会读取到修改后的customer_id,这就是不可重复读。

# 幻读

幻读是指在一个事务中读取了多个数据,但是读取到的数据的数目不同,这是因为另一个事务在当前事务执行期间插入或删除了数据。

例如,如果在一个事务中读取了一个订单表中的所有订单,然后在另一个事务中插入了一个新的订单,最后当前事务再次读取这个订单表中的所有订单,那么就可能会出现幻读。这就是因为在当前事务执行期间,另一个事务插入了一个新的订单,导致当前事务读取到的数据的数目不同。

# 隔离级别

MySql中一共有4种级别,隔离级别从低到高分别是:

  • 读未提交:一个事务还没提交时,它做的变更就能被别的事务看到。
  • 读提交:一个事务提交之后,它做的变更才会被其他事务看到。
  • 可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。
  • 串行化:顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

为什么要设计这么多种呢?因为你隔离得越严实,效率就会越低。因此很多时候,我们都要在二者之间寻找一个平衡点。所以才会有不同的隔离级别供我们选择。

其中读提交解决了脏读,可重复读解决了“不可重复读”,串行化解决了“幻读”。当然串行化也是效率最低的。

# SpringBoot中的隔离级别

在SpringBoot中,可以通过

  • TransactionDefinition.ISOLATION_DEFAULT
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED
  • TransactionDefinition.ISOLATION_READ_COMMITTED
  • TransactionDefinition.ISOLATION_REPEATABLE_READ
  • TransactionDefinition.ISOLATION_SERIALIZE

来设置事务的隔离性。

其中TransactionDefinition.ISOLATION_DEFAULT是事务的默认隔离级别,它会根据数据库的设置来决定事务的隔离级别。在大多数情况下,这个级别就是你想要的级别。

第二种到第五种分别对应数据库中的四种隔离级别:读未提交、读已提交、可重复读和串行化。

MySQL 默认的隔离级别 REPEATABLE READ ,可重复读

  • 设置事务隔离级别
@Transactional(isolation = TransactionDefinition.ISOLATION_DEFAULT)
public class UserService {
  //.......
}
1
2
3
4

以上就是通过注解来实现事务,并设置事务隔离级别——默认。若是编程式事务,则

 public void updateName(){
        // 定义事务属性
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
       // ...
 }
1
2
3
4
5
6

# 事务的传播性

在Spring中,事务的传播性是指当一个事务方法被另一个事务方法调用时,事务该以何种状态存在?是否需要在当前事务中嵌套另一个事务,还是会新开启自己的事务。

举个例子:

@Service
public class DemoService{
  @Autowired
  private UserService userSerice;
  
  @Transactional
  public void test(){
    userSerice.updateName("test");
    int i = 1/0;
  }
  
}
// userSerice.updateName() 该方法也开启了事务
1
2
3
4
5
6
7
8
9
10
11
12
13

当发生异常时,userSerice.updateName()它是否也会回滚。这个就要看事务的传播性设置的何种状态,如果userSerice.updateName它的事务传播属性为是REQUIRED,则它会回滚。若是REQUIRES_NEW,则不会回滚。

如果是REQUIRED,事务的属性都是继承于大事务的

# 7种传播种类

Spring中一共定义了7种方式:

public enum Propagation {
 REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
 SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
 MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
 REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
 NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
 NEVER(TransactionDefinition.PROPAGATION_NEVER),
 NESTED(TransactionDefinition.PROPAGATION_NESTED);
 private final int value;
 Propagation(int value) { this.value = value; }
 public int value() { return this.value; }
}
1
2
3
4
5
6
7
8
9
10
11
12

它们的意思如下:

名称 含义
REQUIRED 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
SUPPORTS 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
MANDATORY 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
REQUIRES_NEW 创建一个新的事务,如果当前存在事务,则把当前事务挂起
NOT_SUPPORTED 以非事务方式运行,如果当前存在事务,则把当前事务挂起
NEVER 以非事务方式运行,如果当前存在事务,则抛出异常
NESTED 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 TransactionDefinition.PROPAGATION_REQUIRED

开启事务后它的传播行为的默认值为:Propagation.REQUIRED

# 传播属性设置

transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
1
@Transactional( propagation= Propagation.REQUIRES_NEW)
public void testAddEx(){
  // .....
}
1
2
3
4

# 回滚规则

Spring中事务的回滚规则默认只有在抛出RuntimeException或Error时,才会发生事务回滚,在遇到检查型(Checked Exception)异常时不会回滚。

而我们可以通过设置回滚规则来自定义抛出哪些异常时进行回滚事务,包括checked异常。

@Transactional(rollbackFor = IOException.class)
public void handle() {
   // .....
}
1
2
3
4

# 超时时间

超时时间是说一个事务允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。

@Transactional(timeout = 10)
public void handle() {
   // .....
}
1
2
3
4

其中时间单位为秒。

# 只读属性

事务的是否只读(read-only)是指事务执行时是否可以修改数据库中的数据。如果一个事务被设置为只读,则事务执行时不能修改数据库中的数据,如果试图修改数据库中的数据,则事务会回滚并抛出异常。

@Transactional(readOnly = true)
public void handle() {
   // .....
}
1
2
3
4

# 事务失效的情况

  1. 非public方法
    • Spring 为方法创建代理、添加事务通知、前提条件都是该方法是 public 的
  2. 调用本类方法(同一个类中A方法调用B方法)
  3. 没有在类上标注@Service、@Component等注解,没有被spring扫描成组件
  4. Exception被捕获了(被catch),没有被抛出
  5. 抛出检查异常导致事务不能正确回滚
    • Spring默认只会回滚--非受检查异常(Unchecked Exception)和错误(继承自 Error)
    • checked Exception受检异常指的都是不受程序直接控制的错误。它们通常都是由于与外部资源/网络交互而发生的,例如数据库问题、网络连接错误、文件丢失等问题,比如:IOException、SQLException、ClassNotFoundException、ParseException
    • 解法:配置回滚规则——rollbackFor
  6. 数据库不支持事务

总结:

spring aop 异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeexception的异常,但可以通过

配置来捕获特定的异常并回滚

上次更新: 2025/04/23, 16:23:16
spring中开启事务
SpringBoot启动流程

← spring中开启事务 SpringBoot启动流程→

最近更新
01
面向切面跟自定义注解的结合
05-22
02
时间跟其他数据的序列化
05-19
03
数据加密与安全
05-17
更多文章>
Theme by Vdoing | Copyright © 2023-2025 EffectTang
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式