《深入浅出springboot2.x》摘录 - 第6章 聊聊数据库事务

本文详细探讨了Spring框架中的事务管理机制,包括@Transactional注解的使用、事务的传播行为、隔离级别及其对数据一致性的影响。并通过实例展示了不同传播行为的效果,帮助读者理解如何在高并发场景下有效利用事务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

运行流程

在这里插入图片描述

@Transactional源码

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;
    
    int timeout() default -1;

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}
  • timeout:事务可以允许的时间戳,单位是秒
  • readOnly:在只读事务执行更新操作会发生异常
  • rollbackForClassName() noRollbackFor() noRollbackForClassName)定义什么异常情况下提交事务,什么异常的情况下回滚事务。

Spring事务管理器

在这里插入图片描述

隔离级别isolation

前言 - 两类丢失更新

  • 第一类丢失更新:由于回滚引发
    - 目前大部分数据库已经克服了第一类丢失更新的问题,也就是现今数据库系统已经不会再出现上述的情况了,不再讨论它。
  • 第二类丢失更新:由于提交引发
    在这里插入图片描述为了克服上述两类问题,数据库提出了事务之间隔离级别的概念。

未提交读

未提交读( read uncommitt )是最低的隔离级别,其含义是允许一个事务读取另外一个事务没有提交的数据。未提交读是一种危险的隔离级别,所以一般在我们实际的开发 应用不广 但是它的优点在于并发能力高,适合那些对数据一致性没有要求而追求高并发的场景。它会产生脏读
在这里插入图片描述

读写提交

读写提交( read committed )隔离级别, 是指一个事务只能读取另外一个事务已经提交的数据。它可以克服脏读
在这里插入图片描述但是读写提交会导致不可重复读
在这里插入图片描述

可重复读

可重复读的目标是克服读写提交中出现的不可重复读的现象,因为在读写提交的时候,可能出些值的变化, 影响当前事务的执行。它能够克服不可重复读的现象
在这里插入图片描述但是它会产生幻读
在这里插入图片描述幻读针对多条记录,而可重复读是针对单条记录

串行化

串行化(Serializable)是数据库最高的隔离级别,它会要求所有的 SQL 都会按照顺序执行,这样就可以克服上述隔离级别出现的各种问题,所以它能够完全保证数据的一致性

总结

在这里插入图片描述

传播行为

在Spring中,当一个方法调用另外一个方法时,可以让事务采取不同的策略工作,如新建事务或者挂起当前事务等,这便是事务的传播行为。

传播行为枚举源码

public enum Propagation {
    /**
     * 需要事务,它是默认传播行为,如果当前存在事务,就沿用当前事务
     * 去否则新建一个事务运行子方法
     */
    REQUIRED(0),
    /**
     * 支持事务,如果当前存在事务,就沿用当前事务
     * 如果不存在,则继续采用无事务的方式运行子方法
     */
    SUPPORTS(1),
    /**
     * 必须使用事务,如果当前没有事务,则会抛出异常,
     * 如果存在当前事务 就沿用当前事务
     */
    MANDATORY(2),
    /**
     * 无论当前事务是否存在,都会创建新事务运行方法
     * 这样新事务就可以拥有新的锁和隔离级别等特性,与当前事务相互独立
     */
    REQUIRES_NEW(3),
    /**
     * 不支持事务,当前存在事务时,将挂起事务,运行方法
     */
    NOT_SUPPORTED(4),
    /**
     * 不支持事务,如果当前方法存在事务,则抛出异常,否则继续使用无事务机制运行
     */
    NEVER(5),
    /**
     * 在当前方法调用子方法时,如果子方法发生异常,
     * 只因滚子方法执行过的 SQL ,而不回滚当前方法的事务
     */
    NESTED(6);

    private final int value;

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

    public int value() {
        return this.value;
    }
}

测试

常用的传播行为只有三种:REQUIREDREQUIRES_NEWNESTED
NESTED 播行为会沿用当前事务的隔离级别和锁等特性,而REQUIRES_NEW 则可以拥有自己独立的隔离级别和锁等特性。

@Transactional 自调用失效问题

直接调用本类方法会导致传播行为失效
在这里插入图片描述在自调用的过程中是类自身的调用 ,而不是代理对象去调用, 那么就不会产生 AOP,这样 Spring就不能把你的代码织入到约定的流程中 于是就产生了现在看到的失败场景。 为了克服这个问题,用一个 Service 去调用另一个Service。
在这里插入图片描述

备注

默认的隔离级别

Oracle 默认的隔离级别为读写提交, MySQL 则是可重复读。

ACID

  • Atomic (原子性) 事务中包含的操作被看作一个整体的 务单元这个业务单元中的操作要么全部成功,要么全部失败,不会出现部分失败、部分成功的场景。
  • Consistency (一致性):事务在完成时,必须使所有的数据都保持一致状态,在数据库中所有的修改都基于事务,保证了数据的完整性。
  • Isolation (隔离性) 这是我们讨论的核心内容,正如上述,可能多个应用程序线程同时访问数据,这样数据库同样的数据就会在各个不同的事务中被访问,这样会产生丢失更新。为了压制丢失更新的产生,数据库定义了隔离级别的概念,通过它的选择,可以在不同程度上压制丢失更新的发生。因为互联网的应用常常面对高并发的场景,所以隔离性是需要掌握的重点内容。
  • Durability (持久性):事务结束后,所有的数据会固化到一个地方,如保存到磁盘当中,即使断电重启后也可以提供给应用程序访问。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值