事务(Spring&MySQL)

事务概念

一组数据库操作要么全部成功,要么全部失败回滚

四大特性

ACID

A:原子性Atomicity,即不可分割

C:一致性Consistency

I:隔离性Isolation

D:持久性Durability

一致性

一致性不是指数据不变,而是事务执行前后数据完整性不变,即业务规则不能被破坏

隔离性

多个事务修改同一数据万一冲突怎么办?

  1. 锁机制:行锁,一个事务修改某行,其他事务阻塞等待;
  2. MVCC 多版本:InnoDB 保存旧版本快照,读的时候读快照,不加锁(快照读)。

持久性

本来不就是写入MySQL了,持久性有什么好说的

你以为 "修改成功写入数据库" 就是写到磁盘了,其实不是。 操作系统有页缓存 : SQL 执行完,数据库先把数据写到内存缓冲区,还没刷到硬盘 。 如果此时数据库进程宕机、服务器断电,内存数据直接消失。 持久性就是靠数据库的redo log:

事务提交前,先把变更日志写入磁盘(redo log 落盘)。就算内存里的数据没刷到数据文件,宕机重启后,数据库读取 redo log 恢复数据。

提交成功 = redo 日志持久到磁盘,不是一定把数据页刷盘。这就是持久性。

Spring事务两大实现方式

1.声明式事务(@Transactional注解)

可加在类/方法上

  • 加类:类中所有 public 方法都继承该事务配置
  • 加方法:仅该方法生效;方法优先级 > 类级别

⚠️ 注解只对 public 方法生效!private/protected 默认不触发 AOP 代理。

2.编程式事务

示例:

java 复制代码
@Service
public class UserService {
    @Autowired
    private TransactionTemplate transactionTemplate;

    public void addUser(){
        transactionTemplate.execute(status -> {
            //数据库操作
            userMapper.insert(...);
            //抛异常自动回滚
            return true;
        });
    }
}

事务传播机制

即A 方法有事务,调用 B 方法(也带 @Transactional),B 用哪种事务

即子方法遇到已有事务时,怎么选择事务

传播类型 含义
REQUIRED(默认) 有事务就加入当前事务;没有就新建事务。最常用
REQUIRES_NEW 新建独立事务,挂起外层事务;内外事务互不影响。外层回滚不影响内层提交
SUPPORTS 有事务就加入;无事务就非事务执行
MANDATORY 必须在已有事务内执行,外层无事务直接抛异常
NOT_SUPPORTED 不使用事务;如果外层有事务,先挂起外层事务执行
NEVER 绝对不能在事务内执行,外层存在事务直接抛异常
NESTED 嵌套事务,基于保存点 savepoint;内层异常回滚到保存点,不会回滚外层。外层回滚,内层一起回滚。区别于 REQUIRES_NEW:不是独立事务

有事务就加入当前事务,什么意思?

场景:A 方法带事务,A 调用 B 方法,B 也是@Transactional(REQUIRED)。 A 已经开启事务了 → B 不会新建事务,直接共用 A 同一个事务,共用同一个数据库连接。

事务隔离级别(区分事务四大特性)

为了解决并发事务问题:脏读、不可重复读、幻读

隔离级别 脏读 不可重复读 幻读
READ_UNCOMMITTED 读未提交 ✅存在 ✅存在 ✅存在
READ_COMMITTED 读已提交 (Oracle 默认) ❌ ✅存在 ✅存在
REPEATABLE_READ 可重复读 (MySQL 默认) ❌ ❌ ✅存在(InnoDB 靠 MVCC 解决大部分幻读)
SERIALIZABLE 串行化 ❌ ❌ ❌

Spring 事务 和 MySQL 事务 的关系 & 区别

Spring 事务是上层封装,MySQL 事务是底层实现。Spring 事务本质就是调用 MySQL 原生事务 API。 没有 MySQL(数据库)的事务能力,Spring 事务完全无法生效。

spring 只是帮你自动写setAutoCommit(false)、commit、rollback。

MySQL 事务 Spring 事务
所属层级 数据库层面 Java 应用框架层面
能力 提供 ACID、锁、MVCC、redo/undo log,真正实现提交回滚 管理事务生命周期,传播行为,AOP 自动控制事务边界
隔离级别 真正生效,数据库实现脏读、幻读控制 Spring 只是把隔离级别参数传给数据库,本身不实现隔离
传播行为 ❌数据库没有传播机制 ✅Spring 特有!数据库完全没有事务传播(REQUIRED/REQUIRES_NEW/NESTED 这些传播规则,是 Spring 在应用层实现)

Spring事务底层原理(AOP+ThreadLocal)

1.容器启动=>扫描带@Transactional的类=>生成动态代理对象

2.调用事务方法时进入代理拦截器 TransactionInterceptor

java 复制代码
【代理前置逻辑】 → 执行业务方法 → 【代理后置逻辑】

3.获取事务资源,ThreadLocal绑定链接

4.执行业务代码(mapper)此时不会自动提交

因为当前线程绑定的连接 autocommit=false,所有 SQL 都在同一个事务里

5.目标方法执行

  • 正常结束:执行 commit 提交事务
  • 抛出异常:执行 rollback 回滚

6.释放资源,清除ThreadLocal绑定

核心:事务资源绑定在线程上,同一个线程内多个方法共用同一个数据库连接

事务失效

核心根源:Spring 事务靠 AOP 动态代理拦截方法。只要没有走代理对象执行目标方法,事务直接失效 。 事务失效 = TransactionInterceptor 没有执行,不会执行开启 / 提交 / 回滚逻辑。

事务的超时、只读 特性

超时

@Transactional(timeout = 10) 单位:秒

含义:事务允许的最长执行时间,超过时间就强制回滚。

⚠️注意:这个超时判断是 Spring 层面,不是 MySQL 的 wait_timeout。

只读

告诉数据库:这个事务只做查询,不做增删改。这是一个提示,不是强制拦截。

有必要吗:

不加注解的话两条 SQL 是两个独立事务,中间别的事务提交修改,第二次查询会读到新数据。 加了 readOnly 事务:两次查询在同一个事务快照,保证可重复读。

相关推荐
frjc1 小时前
Spring WebFlux SSE 长连接,可以替换为 Java 21 虚拟线程吗?
java·人工智能·spring·ai·ai编程
雷一言说2 小时前
Spring AI 2.0 Agent Skills:使 Skill 读取文档、执行脚本
spring
DBA_G2 小时前
GBase 8a MPP集群个人电脑安装环境浅析
数据库
小林ixn2 小时前
别再 MySQL + Milvus 双写了:PostgreSQL + pgvector 一张表搞定 AI 长期记忆
数据库·postgresql·agent
深盾科技_Virbox2 小时前
换机或重装后软件许可怎么恢复:先核对哪四项
java·服务器·数据库
知行产研2 小时前
亚太工程机械市场:中国制造“换道超车“,价值重心从“卖铁“转向“卖系统“
数据库·制造
云贝贝贝2 小时前
白天稳如狗半夜就抖:OceanBase 合并转储把读写延迟顶了上去
数据库·sql·oceanbase
ss2732 小时前
AI时代首先数据库-PostgreSQL实战笔记 | 24 PostgreSQL WAL日志与崩溃恢复原理
数据库·笔记·postgresql
JavaPub-rodert2 小时前
王仕宇分享:Oxigraph 是什么?用 Rust + RocksDB 搭一个轻量 RDF/SPARQL 知识图谱数据库
数据库·rust·知识图谱