Spring 事务

👨‍🏫 参考地址


1. 事务基础

事务能否生效数据库引擎是否支持事务是关键。比如常用的 MySQL 数据库默认使用支持事务的 innodb引擎。但是,如果把数据库引擎变为 myisam,那么程序也就不再支持事务了!

在这里插入图片描述
PS只有保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。换而言之,A、I、D 是手段,C是目的!

在这里插入图片描述

2. Spring 管理事务的方式

  • 编程式事务:在代码中硬编码(在分布式系统中推荐使用) : 通过 TransactionTemplate或者 TransactionManager 手动管理事务,事务范围过大会出现事务未提交导致超时,因此事务要比锁的粒度更小
  • 声明式事务:在 XML 配置文件中配置或者直接基于注解(单体应用或者简单业务系统推荐使用) : 实际是通过 AOP 实现(基于@Transactional 的全注解方式使用最多)

3. Spring 事务管理接口

Spring框架中,事务管理相关最重要的 3 个接口如下:

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

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


💖 PlatformTransactionManager:事务管理接口

Spring 并不直接管理事务,而是提供了多种事务管理器 。Spring 事务管理器的接口是PlatformTransactionManager

通过这个接口,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 个方法以及一些表示事务属性的常量比如隔离级别、传播行为等等。

package org.springframework.transaction;

import org.springframework.lang.Nullable;

public interface TransactionDefinition {
    int PROPAGATION_REQUIRED = 0;
    int PROPAGATION_SUPPORTS = 1;
    int PROPAGATION_MANDATORY = 2;
    int PROPAGATION_REQUIRES_NEW = 3;
    int PROPAGATION_NOT_SUPPORTED = 4;
    int PROPAGATION_NEVER = 5;
    int PROPAGATION_NESTED = 6;
    int ISOLATION_DEFAULT = -1;
    int ISOLATION_READ_UNCOMMITTED = 1;
    int ISOLATION_READ_COMMITTED = 2;
    int ISOLATION_REPEATABLE_READ = 4;
    int ISOLATION_SERIALIZABLE = 8;
    int TIMEOUT_DEFAULT = -1;
    // 返回事务的传播行为,默认值为 REQUIRED。
    int getPropagationBehavior();
    //返回事务的隔离级别,默认值是 DEFAULT
    int getIsolationLevel();
    // 返回事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
    int getTimeout();
    // 返回是否为只读事务,默认值为 false
    boolean isReadOnly();

    @Nullable
    String getName();
}

💖 TransactionStatus:事务状态

TransactionStatus接口用来记录事务的状态,该接口定义了一组方法,用来获取或判断事务的相应状态信息。PlatformTransactionManager.getTransaction(…)方法返回一个 TransactionStatus 对象。

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

⭐ 事务的传播行为

事务传播行为是为了解决业务层方法之间互相调用的事务问题。

枚举类:Propagation

package org.springframework.transaction.annotation;

import org.springframework.transaction.TransactionDefinition;

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),//事务失效
    
	// 如果当前存在事务,就在嵌套事务内执行;如果当前没有事务,则新建一个事务
	// 有则嵌套,无则新建
	// a 中调 b,事务嵌套传播,a回滚 b也回滚,b回滚 a不回滚(b回滚 只是 a局部回滚)
    NESTED(TransactionDefinition.PROPAGATION_NESTED);

    private final int value;

    Propagation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}
  • 事务正常传播的行为

    • REQUIRED
    • REQUIRES_NEW
    • NESTED
    • MANDATORY
  • 事务不会回滚的传播行为

    • SUPPORTS
    • NOT_SUPPORTED
    • NEVER

⭐ 事务的隔离级别

在这里插入图片描述

  • 读未提交
  • 读已提交:解决脏读
  • 可重复读:解决脏读、不可重复读、幻读?(快照读 MVCC 或者 开启读写锁 可解决)
  • 串行化:解决脏读、不可重复读、幻读

⭐ 事务超时

所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒,默认值为-1这表示事务的超时时间取决于底层事务系统或者没有超时时间。

⭐ 事务只读属性

package org.springframework.transaction;

import org.springframework.lang.Nullable;

public interface TransactionDefinition {
    ......
    // 返回是否为只读事务,默认值为 false
    boolean isReadOnly();
}

对于只有读取数据查询的事务,可以指定事务类型为 readonly,即只读事务只读事务不涉及数据的修改,数据库会提供一些优化手段,适合用在有多条数据库查询操作的方法中。

为什么数控查询操作还需要事务支持呢?

在这里插入图片描述

⭐ 事务回滚规则

这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常(RuntimeException 的子类)时才会回滚,Error 也会导致事务回滚,但是,在遇到检查型(Checked)异常时不会回滚。

在这里插入图片描述

自定义回滚异常类型

@Transactional(rollbackFor= MyException.class)

💖 @Transactional 注解

⭐ 作用范围

  • 方法:推荐将注解使用于方法上,不过需要注意的是:该注解只能应用到 public 方法上否则不生效
  • :如果这个注解使用在类上的话,表明该注解对该类中所有的 public 方法都生效。
  • 接口:不建议,实现类会继承注解?

⭐ 常用配置参数

在这里插入图片描述

⭐ 事务注解实现原理 ⭐

@Transactional 的工作机制是基于 AOP 实现的,AOP 又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果目标对象没有实现了接口,会使用 CGLIB 动态代理。

如果一个类或者一个类中的 public 方法上被标注@Transactional 注解的话,Spring 容器就会在启动的时候为其创建一个代理类,在调用被@Transactional 注解的 public 方法的时候,实际调用的是,TransactionInterceptor 类中的 invoke()方法。这个方法的作用就是在目标方法之前开启事务,方法执行过程中如果遇到异常的时候回滚事务,方法调用完成之后提交事务。


Spring AOP 自调用问题

当一个方法被标记了@Transactional 注解的时候,Spring 事务管理器只会在被其他类方法调用的时候生效,在同一个类中方法调用事务就失效了

MyService 类中的method1()调用method2()就会导致method2()事务失效

@Service
public class MyService {

private void method1() {
     method2();// 同一个类中的方法调用,代理对象就不会拦截方法
     //......
}
@Transactional
 public void method2() {
     //......
  }
}

特例

@Service
public class MyService {

private void method1() {
	 // 先获取该类的代理对象,然后通过代理对象
     ((MyService)AopContext.currentProxy()).method2(); 调用method2。
     //......
}
@Transactional
 public void method2() {
     //......
  }
}

在这里插入图片描述


在这里插入图片描述

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值