事务(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 事务:两次查询在同一个事务快照,保证可重复读。

相关推荐
时间的拾荒人10 小时前
Redis 进阶实战:渐进式遍历、持久化机制与事务详解
数据库·redis·缓存
普通人610 小时前
MySQL_FIND_IN_SET函数详解
android·数据库·mysql
真上帝的左手10 小时前
4. 关系型数据库-MySQL-Canal 进阶
数据库·mysql·canal
xiaoye-duck11 小时前
《C++ 项目:手写大模型接入 SDK》DataManager数据管理模块:SQLite持久化存储
开发语言·数据库·c++·sqlite
codeejun11 小时前
每日一Go·MySQL-14、慢 SQL 产生原因全解
sql·mysql·golang
唐老板12 小时前
uuidv7 来了:为什么别再拿 uuidv4 当主键了
数据库
SelectDB1 天前
让变更流出 Doris:Apache Doris 5.0 基于增量计算的 CDC 技术解析
数据库
这个DBA有点耶1 天前
MCP隔离层+行级语义审计+输出校验:AI访问数据库的安全边界拆解
数据库·人工智能·ai编程
新*1 天前
Elasticsearch 7.17 的 SQL 到底能查到什么?一次踩坑记录
后端·sql·elasticsearch·查询