Spring 事务

Spring 事务管理

事务就是一系列操作的原子性执行,要么全部完成,要么全部不完成,不会结束在中间某个点。

Spring 事务管理是主要基于以下概念:

  • 事务的传播行为(Propagation Behavior)

    定义了当一个事务方法被另一个事务方法调用时,事务如何传播,例如加入已存在的事务或创建一个新的事务。

  • 事务的隔离级别(Isolation Level)

    定义了事务在执行过程中对数据的可见性,以及并发事务之间的隔离程度。

  • 事务的超时机制(Timeout)

    定义了事务允许的最大执行时间,超过这个时间事务将被自动回滚。

  • 事务的只读属性(Read-only)

    标记事务为只读,这可以提高事务的性能,并且在某些情况下可以避免数据不一致。

  • 事务的回滚规则(Rollback Rules)

    定义了哪些异常会导致事务回滚,哪些异常不会。

Spring 事务管理的实现方式

  • 1.声明式事务管理

    声明式事务将事务管理代码从业务方法中分离出来,通过aop进行封装。

    Spring 声明式事务使得我们无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作。

    • 注解方式(推荐)

      目前主流的方式是通过 @Transactional 注解实现。

      (1) 配置启用事务管理,通过配置类上的 @EnableTransactionManagement 注解来实现。
      (2) 定义事务管理器(PlatformTransactionManager接口的实现类,例如DataSourceTransactionManager)
      (3) 在类或方法上使用@Transactional注解,定义事务的传播行为、隔离级别、超时时间、只读属性、回滚规则等

        Spring配置类
        
        import org.springframework.context.annotation.Configuration;
      
        @Configuration
        @EnableTransactionManagement //1)启用事务管理
        public class SpringConfig {
            
            //2)定义事务管理器
            @Bean
            public PlatformTransactionManager transactionManager(DataSource datasource){
              return new DataSourceTransactionManager(dataSource);
            }
        }
        
        实现类
      
        import org.springframework.transaction.annotation.Transactional;
      
        public class UserService {
      
            @Transactional(rollbackFor = Exception.class)//查看源码了解所有属性配置
            public void updateUser(User user) {
                // 数据更新操作
            }
        }
      
          
      
    • xml配置文件的实现方式,已不推荐使用。

  • 2.编程式事务管理

    • 通过 PlatformTransactionManager 接口手动管理事务的创建、提交和回滚。这种方式更灵活,但代码更复杂。

    • 通过TransactionTemplate的excute方法管理事务。

Spring 的事务传播行为

在TransactionDefinition接口中定义了七个事务传播行为:

  • PROPAGATION_REQUIRED 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。如果嵌套调用的两个方法都加了事务注解,并且运行在相同线程中,则这两个方法使用相同的事务中。如果运行在不同线程中,则会开启新的事务。(默认)

  • PROPAGATION_SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。

  • PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果不存在事务,则抛出异常IllegalTransactionStateException。

  • PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。需要使用JtaTransactionManager作为事务管理器。

  • PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。需要使用JtaTransactionManager作为事务管理器。

  • PROPAGATION_NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常。

  • PROPAGATION_NESTED 如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务, 则按PROPAGATION_REQUIRED 属性执行。

Spring 事务隔离级别

在TransactionDefinition接口中定义了五个隔离级别(实际只有四个隔离级别)

  • ISOLATION_DEFAULT 使用底层数据库的默认隔离级别。

  • ISOLATION_READ_UNCOMMITTED 读取未提交数据,可能会出现脏读, 不可重复读、幻读。基本不适用

  • ISOLATION_READ_COMMITTED 读取已提交数据,可能会出现不可重复读和幻读。Oracle默认

  • REPEATABLE_READ 可重复读,会出现幻读。 MySQL默认

  • Isolation.SERIALIZABLE 串行化

并发事务的带来的事务问题

在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对统一数据进行操作)。并发虽然是必须的,但可能会导致一下的问题。

  • 脏读(Dirty read): 指的是A事务读取到B事务尚未提交的数据,“脏数据”指的是未提交的数据。

  • 不可重复读(Unrepeatableread): 指的是A事务使用相同条件多次读取同一数据,得到的结果不一致。

  • 幻读(Phantom read): 指的是A事务使用相同条件多次读取某一批数据,得到的记录条数不一致。幻读与不可重复读类似,区别在于不可重复读的重点是修改,幻读的重点在于新增或者删除。

不可重复度和幻读区别:

例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。

例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2 又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读。

事务的隔离级别与并发问题的关系

隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read)
未提交读(Read uncommitted)
已提交读(Read committed)
可重复读(Repeatable read)
可串行化(Serializable )

Spring 事务的失效场景

  • 一 事务不生效

    • 事务方法非public访问权限

    • 事务方法被关键字final修饰

    • 在同一个类中的方法直接内部调用,调用方未开启事务

    • 事务方法的对象未被Spring IoC容器管理

    • 数据库不支持事务

    • 项目未开启事务支持

  • 二 事务不回滚

    • 设置的事务传播类型不支持事务,只有REQUIRED,REQUIRES_NEW,NESTED这三种传播类型才会创建新事务

    • 异常被手动捕获,如try..catch

    • 异常被手动捕获后,抛出的异常非RuntimeException(运行时异常)或Error(错误)

    • 抛出的异常不属于自定义的事务回滚异常

    • 嵌套事务,回滚多了。(理解请看参考)

注:不同版本可能会有不同的结果。建议具体项目,具体分析。

参考链接

posted @ 2024-08-27 13:58  抒写  阅读(16)  评论(0编辑  收藏  举报