Spring5应用之事务属性

2023年 10月 12日 105.6k 0

作者简介:☕️大家好,我是Aomsir,一个爱折腾的开发者!
个人主页:Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏
当前专栏:Spring5应用专栏_Aomsir的博客

参考文献

  • 孙哥suns说Spring5~学不会Spring? 因为你没找对人~孙帅
  • Spring官方文档

前言

在前两篇文章中,我们深入探索了Spring与MyBatis的整合技术,并详细讨论了Spring在事务处理方面的能力。今天,我决定进一步深入研究Spring,在事务属性上进一步的深入研究

事务属性

什么是事物属性?

事务属性是用来描述事务特性的一组值。主要包括隔离属性传播属性只读属性超时属性异常属性。通过这些属性,我们可以全面地描述和管理事务的行为

如何添加事务属性?

在上一篇文章中,我们已经探讨了如何在原始方法的类或方法上使用@Transaction注解来定义事务的切入点。要设置事务属性,我们只需在这一注解中配置相应的属性值。例如,isolation用于指定隔离级别,propagation表示传播行为,readOnly标识该事务是否为只读,rollbackFor定义哪些异常会触发事务回滚,而timeOut指定事务的超时时间。接下来,我们会详细解析每一个属性的具体值和含义,以帮助大家更深入地理解和使用事务属性

隔离属性

隔离属性主要描述了事务如何解决数据库事务并发操作中的问题。在此之前,我们首先要明确什么是并发,以及并发中可能会遇到哪些问题,以及如何解决这些问题。

什么是并发? 并发是指多个事务在同一时刻,对相同的数据进行访问和操作。

并发会产生哪些问题? 并发操作中可能出现的问题包括脏读不可重复读幻影读

并发问题如何解决? 并发中的问题可以通过设定合适的隔离属性来解决。根据隔离属性的不同值,我们可以有效地避免或解决在并发处理过程中出现的这些问题

脏读

所谓脏读,是指一个事务读取到了另一个事务尚未提交的数据,这种操作可能导致当前事务中的数据不一致。想象一个场景:如果事务的隔离级别设置得不够高,它可能会读取到另一事务已修改但尚未提交的数据。如果在此基础上,我们对这份数据进行了进一步的修改、添加或删除,而后来那个外部事务决定回滚,那么当前事务所做的所有操作都可能变得无效或导致数据错误

解决方案

为了避免脏读,可以将事务的隔离属性设置为“读已提交”。这样,事务只能读取其他事务已经提交的数据,从而确保数据的一致性。在代码中,这可以通过以下注解属性来实现:

@Transaction(isolation=Isolation.READ_COMMITTED)

不可重复读

所谓不可重复读,是指在同一个事务内多次读取相同数据时,得到的结果不同。举个例子,假设在事务A中,第一次查询某数据得到的结果是1000。而在事务A的两次查询之间,另外一个事务B将这个数据减少了200。当事务A再次查询时,得到的结果就变成了800,从而导致事务A中出现了数据不一致的情况

解决方案

为了避免不可重复读的问题,我们可以将事务的隔离属性设置为“可重复读”。这样的设置确保在事务执行过程中,对同一数据的多次读取都能获得一致的结果。在代码中,可以通过以下注解属性来实现:

其本质是为相关数据加上行锁,确保在当前事务执行期间,其他事务不能修改这部分数据

@Transaction(isolation=Isolation.REPEATABLE_READ)

幻影读

所谓幻影读,是指在同一个事务中对整张表进行多次统计查询时,得到的结果各不相同,从而在事务内产生数据不一致的问题

解决方案

为了避免幻影读的问题,我们可以将事务的隔离属性设置为“串行化”(SERIALIZABLE)。这样的设置可以确保在事务执行过程中,对整张表的多次统计查询都能获得一致的结果。在代码中,可以通过以下注解来实现:

其核心机制是为整张表加上表锁,确保在当前事务执行期间,其他事务不能对此表进行添加或删除操作。

@Transaction(isolation=Isolation.SERIALIZABLE)

注意事项

  • 数据库默认隔离级别: Spring提供的事务属性默认值会根据不同类型的数据库的默认隔离级别进行动态选择。
  • 灵活选择隔离属性: 在实际应用中,我们应当灵活地选择隔离属性。根据具体的业务需求,选择合适的隔离属性,当然,默认的设置通常也是推荐的。
  • 解决并发问题: 在实战中遇到并发问题时,常用的解决方法是采用悲观锁。例如,可以使用JPA的Version或者通过MyBatis的拦截器进行自定义开发
  • 传播属性

    传播属性主要描述了如何解决事务嵌套的问题。事务嵌套发生在一个主事务中包含多个子事务的情况。

    例如,当AService中的a方法启动一个事务时,这个a方法可能会调用BService的b方法和CService的c方法,这两个方法都各自拥有独立的事务,这种情况下就构成了事务嵌套。这样的嵌套可能导致各个子事务之间相互影响,进而使主事务失去其原子性。

    通过正确地设置传播属性,我们可以确保在整个操作过程中只存在一个统一的事务。

    解决方案

    当我们需要设定事务的传播行为,可以在事务注解中添加propagation属性。各个传播属性的具体值及其含义可以参考下表。

    在实际开发中,REQUIREDSUPPORTS这两个传播属性可以解决99%的场景需求。

    默认的传播属性是REQUIRED,因此对于增、删、改的方法,我们通常不需要手动设置。而对于查询方法,我们则需要手动将其传播属性设置为SUPPORTS
    在这里插入图片描述

    只读属性

    对于仅涉及查询操作的业务方法,我们可以添加只读属性,从而优化运行效率。这可以通过readOnly属性来实现。虽然它的默认值是false,但在需要的时候,我们可以手动开启它,使查询操作更加高效

    超时属性

    超时属性定义了事务执行的最长允许时间。这在某些场景下尤为关键,例如,当当前事务所需的数据被另一个事务或代码锁定时,本事务可能需要等待并尝试加锁。 我们可以通过timeout属性来设置超时限制,其单位为秒。设定超时属性后,为了验证其效果,我们可以在业务方法中插入线程休眠代码来进行模拟和测试

    异常属性

    异常属性指定了在事务中发生何种异常时应执行回滚操作,以及在哪些异常下应提交数据。我们可以通过属性rollbackFornoRollbackFor来进行设置。

    在Spring的事务管理中,异常属性的默认设置是

    • 当事务中发生RuntimeException及其子类的异常时,系统会自动进行回滚
    • 当事务中发生Exception及其子类的异常时,则默认执行提交操作。

    对于大多数实际场景,建议沿用此默认值,尤其是对于RuntimeException及其子类

    总结

    经过上述详细的探讨,我们对Spring事务中的各项属性有了深入的了解。现在,我们来做一个简明扼要的总结:

  • 隔离属性:推荐使用默认值。
  • 传播属性:对于增删改操作,建议直接使用默认值,即REQUIRED。而对于查询操作,应手动设置为SUPPORTS。
  • 只读属性:对于增删改操作,使用默认值,即false;对于查询操作,应手动设置为true。
  • 超时属性:根据具体业务需求来决定,但默认值为-1。
  • 异常属性:建议使用默认设
    因此,总结如下:
    • 对于增删改操作:@Transaction
    • 对于查询操作:@Transaction(propagation=Propagation.SUPPORTS, readOnly=true)

    相关文章

    Oracle如何使用授予和撤销权限的语法和示例
    Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
    下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
    社区版oceanbase安装
    Oracle 导出CSV工具-sqluldr2
    ETL数据集成丨快速将MySQL数据迁移至Doris数据库

    发布评论