昔年浅巷

昔年浅巷

Spring事务简单回顾

11
2024-10-19

前言

事务的特性

  • 原子性Atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
  • 一致性Consistency):执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
  • 隔离性Isolation):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
  • 持久性Durability):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。

只有保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。也就是说 A、I、D 是手段,C 是目的!

1709039934253.webp

Mysql数据库对事务的支持

innodb 引擎 支持事务

myisam 引擎 不支持事务

在 MySQL 中,恢复机制是通过 回滚日志(undo log) 实现的,所有事务进行的修改都会先记录到这个回滚日志中,然后再执行相关的操作。如果执行过程中遇到异常的话,我们直接利用 回滚日志 中的信息将数据回滚到修改之前的样子即可!并且,回滚日志会先于数据持久化到磁盘上。这样就保证了即使遇到数据库突然宕机等情况,当用户再次启动数据库的时候,数据库还能够通过查询回滚日志来回滚之前未完成的事务。

Spring对事务的支持

Spring的事务分为两种类型,编程式事务与注解自动事务,他们各有优缺点,相辅相成。

编程式(手动)事务

通过 TransactionTemplate或者TransactionManager手动管理事务

TransactionTemplate管理事务

@Autowired
private TransactionTemplate transactionTemplate;
public void testTransaction() {

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {

                try {

                    // ....  业务代码
                } catch (Exception e){
                    //回滚
                    transactionStatus.setRollbackOnly();
                }

            }
        });
}

PlatformTransactionManager管理事务

@Autowired
private PlatformTransactionManager transactionManager;

public void testTransaction() {

  TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
          try {
               // ....  业务代码
              transactionManager.commit(status);
          } catch (Exception e) {
              transactionManager.rollback(status);
          }
}

优点:

  • 灵活性高,可以根据具体的业务需求来灵活控制事务的行为。
  • 可以实现更细的粒度控制,如通过PlatformTransactionManager或TransactionTemplate来控制事务。
  • 适合业务量大且功能复杂的场景,因为每次实现都需要单独实现,对于功能复杂的业务逻辑来说,使用编程式事务可以避免复杂的配置。

缺点:

  • 代码冗长,可读性差,容易出现错误。
  • 需要开发人员理解事务管理的底层机制,并编写具体的代码。

注解式(自动)事务

代码侵入性最小

@Transactional(propagation = Propagation.REQUIRED)
public void aMethod {
  //do something
  B b = new B();
  C c = new C();
  b.bMethod();
  c.cMethod();
}

优点:

  • 不需要通过编程的方式管理事务,减少了样板式代码,提高了代码的清晰性和可维护性。
  • 相对来说比较容易上手,开发人员只需要学习注解即可。
  • 声明式事务属于非侵入性,不会影响业务逻辑的实现,只需通过基于@Transactional注解的方式(或在配置文件中做相关的事务规则声明),便可以将事务规则应用到业务逻辑中。
  • 易于扩展:可以通过AOP技术轻松地扩展使其支持新的事务策略。

缺点:

  • 最细粒度只能是作用到方法级别,无法做到像编程事务那样可以作用到代码块级别。
  • 事务属性需要在所有方法中声明,这可能会导致事务属性的冗余。
  • 如果忘记在所有与关系数据库交互的公共Service层方法中使用@Transactional注解,业务方法可能会跨越多个数据库事务,从而影响原子性。
  • 如果使用了final修饰符,那么在代理类中就无法重写方法,从而无法添加事务功能。

小结:

综上所述,选择哪种事务管理方式取决于具体的业务场景和开发需求。对于那些对事务粒度要求不高、希望减少代码复杂性的简单业务场景,声明式事务可能是更好的选择。而对于那些需要高度控制事务的复杂场景,或者希望能够灵活地根据业务需求调整事务的细粒度,编程式事务则更为合适。

Spring事务的“三兄弟”接口

  • PlatformTransactionManager:(平台)事务管理器,Spring 事务策略的核心。
  • TransactionDefinition:事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)。
  • TransactionStatus:事务运行状态。

我们可以把 PlatformTransactionManager 接口可以被看作是事务上层的管理者,而 TransactionDefinitionTransactionStatus 这两个接口可以看作是事务的描述。

PlatformTransactionManager 会根据 TransactionDefinition 的定义比如事务超时时间、隔离级别、传播行为等来进行事务管理 ,而 TransactionStatus 接口则提供了一些方法来获取事务相应的状态比如是否新事务、是否可以回滚等等。

PlatformTransactionManager 事务管理器

Spring 并不直接管理事务,而是提供了多种事务管理器 。
通过这个接口,Spring 为各个平台如:JDBC(DataSourceTransactionManager)、Hibernate(HibernateTransactionManager)、JPA(JpaTransactionManager)等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。

PlatformTransactionManager接口中定义了三个方法:

package org.springframework.transaction;

import org.springframework.lang.Nullable;

public interface PlatformTransactionManager {
    //获得事务
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
    //提交事务
    void commit(TransactionStatus var1) throws TransactionException;
    //回滚事务
    void rollback(TransactionStatus var1) throws TransactionException;
}

TransactionDefinition 事务属性

事务管理器接口 PlatformTransactionManager 通过 getTransaction(TransactionDefinition definition) 方法来得到一个事务,这个方法里面的参数是 TransactionDefinition 类 ,这个类就定义了一些基本的事务属性。

什么是事务属性呢? 事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。

事务属性包含了 5 个方面:

  • 隔离级别
  • 传播行为
  • 回滚规则
  • 是否只读
  • 事务超时

TransactionDefinition 接口中定义了 5 个方法以及一些表示事务属性的常量比如隔离级别、传播行为等等。

传播类型描述
PROPAGATION_REQUIRED支持一个已经存在的事务。如果没有事务,则创建一个新的事务;
PROPAGATION_SUPPORTS支持一个已经存在的事务。如果没有事务,则以非事务方式运行;
PROPAGATION_MANDATORY支持一个已经存在的事务。如果没有活动事务,则抛异常;
PROPAGATION_REQUIRES_NEW始终开始新事务。如果活动事物已经存在,将其暂停;
PROPAGATION_NOT_SUPPORTED不支持活动事务的执行。始终以非事务方式执行,并暂停任何现有事务;
PROPAGATION_NEVER即使存在活动事务,也始终以非事务方式执行。如果存在活动事物,抛出异常;
PROPAGATION_NESTED如果存在活动事务,则在嵌套事务中运行。如果没有活动事务,则与PROPAGATION_REQUIRED相同;

拓展:事务隔离级别

隔离级别描述
ISOLATION_DEFAULT底层存储的默认隔离级别
ISOLATION_READ_UNCOMMITTEDRU:最低级隔离级别。它几乎不是事务,允许查看一个事务未提交事务修改的数据;
ISOLATION_READ_COMMITTEDRC:大多数数据库的默认级别。不可以读取未提交事务的数据,一但提交其他事务就可以操作改数据;
ISOLATION_REPEATABLE_READRR:比RC级别更严格,确保一个事务的重复读取数据都是一致的,不能避免‘幻读’
ISOLATION_SERIALIZABLES:最严格的级别,所有事务排队执行;(数据操作最安全,性能最差的一个)

TransactionStatus 事务状态

TransactionStatus接口用来记录事务的状态 该接口定义了一组方法,用来获取或判断事务的相应状态信息。

public interface TransactionStatus{
    boolean isNewTransaction(); // 是否是新的事务
    boolean hasSavepoint(); // 是否有恢复点
    void setRollbackOnly();  // 设置为只回滚
    boolean isRollbackOnly(); // 是否为只回滚
    boolean isCompleted; // 是否已完成
}

@Transactional 注解的变量参数

属性名说明
propagation事务的传播行为,默认值为 REQUIRED,可选的值在**TransactionDefinition**
isolation事务的隔离级别,默认值采用 DEFAULT,可选的值在**Isolation**
timeout事务的超时时间,默认值为-1(不会超时)。如果超过该时间限制但事务还没有完成,则自动回滚事务。
readOnly指定事务是否为只读事务,默认值为 false。
rollbackFor用于指定能够触发事务回滚的异常类型,并且可以指定多个异常类型。

参考文献

Spring 事务详解