MySQL与Spring,事务与自动提交有什么关系?

MySQL与Spring,事务与自动提交有什么关系?

  • 一、MySQL事务
    • [1. MySQL事务的开启与结束](#1. MySQL事务的开启与结束)
    • [2. 自动提交与事务的关系](#2. 自动提交与事务的关系)
      • [2.1 自动提交的机制](#2.1 自动提交的机制)
      • [2.2 自动提交与会话](#2.2 自动提交与会话)
      • [2.3 `BEGIN`/`START TRANSACTION`与自动提交的关系](#2.3 BEGIN/START TRANSACTION与自动提交的关系)
  • 二、Spring事务管理机制深度解析
    • [1. Spring事务管理架构](#1. Spring事务管理架构)
    • [2. Spring事务中setAutoCommit(false)的本质](#2. Spring事务中setAutoCommit(false)的本质)
      • [2.1 Spring事务管理器源码简介](#2.1 Spring事务管理器源码简介)
      • [2.2 `setAutoCommit(false)` ≠ 开启事务](#2.2 setAutoCommit(false) ≠ 开启事务)
      • [2.3 为什么需要`setAutoCommit(false)`?](#2.3 为什么需要setAutoCommit(false)?)
      • [2.4 为什么Spring不直接使用START TRANSACTION?](#2.4 为什么Spring不直接使用START TRANSACTION?)
    • [3. Spring事务与MySQL事务的对应关系](#3. Spring事务与MySQL事务的对应关系)
    • [4. Spring事务传播行为与连接绑定](#4. Spring事务传播行为与连接绑定)
    • [5. Spring事务的完整生命周期](#5. Spring事务的完整生命周期)
  • 三、总结与最佳实践
    • [1. 核心要点总结](#1. 核心要点总结)
    • [2. 性能与注意事项](#2. 性能与注意事项)

双生子文章MySQL事务与Spring事务的关系


一、MySQL事务

1. MySQL事务的开启与结束

MySQL开启事务的方式

  • 显式开启事务 :使用 STRAT TRANSACTIONBEGIN 显式的开启事务,COMMIT 或 ROLLBACK 来控制事务的结束。
  • 隐式事务 :隐式事务指的是,在 关闭自动提交(SET autocommit = 0) 模式后,后续SQL语句默认属于同一个事务。SET autocommit = 0 命令不是直接开启事务的命令,而是设置事务模式
  • 事务开启的"本质"是关闭自动提交并非执行 START TRANSACTION,START TRANSACTION 是显式标记一个事务的开始,而关闭自动提交时让数据库知道"接下来的SQL语句需要一起提交"

事务结束命令

  • COMMIT:提交事务,让所有修改永久生效。
  • ROLLBACK:回滚事务,撤销所有未提交的修改。
  • 隐式提交:某些语句(如DDL:CREATE、ALTER、DROP等)会自动提交当前事务。

命令对比

sql 复制代码
-- 方式1:使用START TRANSACTION
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;// 提交事务

-- 方式2:使用BEGIN
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;

-- 方式3:START TRANSACTION 支持的特性选项
START TRANSACTION READ ONLY;     -- 开启只读事务
START TRANSACTION READ WRITE;    -- 开启读写事务(默认)
START TRANSACTION WITH CONSISTENT SNAPSHOT; -- 开启一致性快照事务(InnoDB)

-- 方式4:通过设置自动提交模式隐式开启事务
-- 4.1 查看当前自动提交状态
SHOW VARIABLES LIKE 'autocommit';

-- 4.2 关闭自动提交(默认为ON/1)
SET autocommit = 0;  -- 或 SET autocommit = OFF;

-- 此时每条SQL语句都不会自动提交,需要显式COMMIT
UPDATE accounts SET balance = 1000 WHERE id = 1;
UPDATE accounts SET balance = 2000 WHERE id = 2;
COMMIT;// 提交事务

-- 恢复自动提交
SET autocommit = 1;

2. 自动提交与事务的关系

2.1 自动提交的机制

默认状态 :MySQL默认开启自动提交(autocommit = 1),也就是每条SQL语句在执行后会立即自动提交。

自动提交的含义

sql 复制代码
-- 当 autocommit=1(默认开启自动提交) 时,下面每条语句都是一个独立的事务
UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- 自动提交
UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- 自动提交

-- 当 autocommit=0 时,需要显式提交
SET autocommit = 0;// 关闭自动提交
UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- 未提交
UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- 未提交
COMMIT; -- 手动提交

2.2 自动提交与会话

MySQL的【自动提交】默认是开启的,不过我们也可以在配置文件中进行全局的设置。我们这里说的【自动提交】是 会话级别 的设置:

  • 每个数据库连接(Connection)都有自己的自动提交状态。
  • 连接的自动提交状态不会影响其他连接。
  • 连接关闭后重新建立,会恢复为默认的自动提交状态。

2.3 BEGIN/START TRANSACTION与自动提交的关系

sql 复制代码
-- 即使 autocommit = 1,使用BEGIN也会暂时关闭自动提交
SET autocommit = 1; -- 默认状态:开启自动提交模式。

BEGIN; -- 此时自动提交被暂时挂起
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- 这里不会自动提交

COMMIT; -- 提交事务,提交后自动提交状态恢复为1

-- 如果没有BEGIN,直接执行UPDATE会立即自动提交
UPDATE accounts SET balance = 1000 WHERE id = 1; -- 立即提交

二、Spring事务管理机制深度解析

1. Spring事务管理架构

复制代码
┌──────────────────────────────────────────────────────────────────┐
│                  Spring Transaction Management				   │
├──────────────────────────────────────────────────────────────────┤
│ @Transactional(声明式事务) / TransactionTemplate(编程式事务)     │
├──────────────────────────────────────────────────────────────────┤
│  PlatformTransactionManager (事务管理器接口)                     │
│  ├─ DataSourceTransactionManager (实现)                          │
│  ├─ JpaTransactionManager                              		   │
│  └─ JtaTransactionManager                                        │
├──────────────────────────────────────────────────────────────────┤
│  DataSource (数据源) /                                           │
│  Connection Pool (数据库连接池,比如,Druid, HikariCP)          │
├──────────────────────────────────────────────────────────────────┤
│  JDBC Connection                                      		   │
└──────────────────────────────────────────────────────────────────┘

2. Spring事务中setAutoCommit(false)的本质

2.1 Spring事务管理器源码简介

我们可以从 编程式事务:【事务模板TransactionTemplate】的核心方法 execute(TransactionCallback action) 看起:

java 复制代码
// Spring DataSourceTransactionManager 源码关键片段(有部分替换,剔除掉了复杂的调用过程)
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager {

    protected void doBegin(Object transaction, TransactionDefinition definition) {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
        Connection con = null;

        try {
            // 1. 从连接池获取连接
            con = this.dataSource.getConnection();

            // 2. 关闭自动提交 - 核心代码!
            if (con.getAutoCommit()) {
                txObject.setMustRestoreAutoCommit(true);
                // 【核心】:就是在这里去告诉MySQL不要自动提交,而是需要手动控制事务。
                con.setAutoCommit(false);
            }

            // 3. 设置隔离级别(如果需要)
            Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(
                    con, definition);

            // 4. 其他配置...
        }
        catch (Throwable ex) {
            // 异常处理
        }
    }
}

对于上面的源码,Spring事务管理器(DataSourceTransactionManager)在获取到数据库连接后执行的 connection.setAutoCommit(false),就是在告诉 MySQL "不要自动提交,需要手动控制事务"

1. 为什么是这样?

  • MySQL默认行为:MySQL默认自动提交,每条SQL语句独立提交。
  • 事务需求 :++为了实现ACID中的原子性,需要将多个SQL语句视为一个整体++。
  • 实现方式:通过关闭自动提交,使后续SQL语句不立即生效,而是等待COMMIT才永久生效。

2. 技术本质setAutoCommit(false) 是 Java JDBC API 对 MySQL 的 SET autocommit = 0命令 的封装,是 Spring事务管理 实现的核心机制。它告诉数据库"接下来的 SQL语句 属于同一个事务,不要自动提交"


2.2 setAutoCommit(false) ≠ 开启事务

代码层面的理解

java 复制代码
Connection conn = dataSource.getConnection();
conn.setAutoCommit(false); // 这只是准备阶段!

// 此时MySQL中"事务"并没有被开启
// 直到执行第一条SQL语句,MySQL才真正开始事务

conn.createStatement().executeUpdate(
    "UPDATE accounts SET balance = balance - 100 WHERE id = 1");
// ↑ 执行这第一条SQL时,MySQL才真正开始事务

// 后续SQL都在同一个事务中
conn.createStatement().executeUpdate(
    "UPDATE accounts SET balance = balance + 100 WHERE id = 2");
    
conn.commit(); // 提交事务

MySQL的实际行为

sql 复制代码
-- 当Spring执行 setAutoCommit(false) 时,MySQL层面:
-- 1. 只是设置了连接的属性,没有 start transaction / begin 语句
-- 2. 连接处于 "非自动提交" 模式。

-- 当执行第一条INSERT/UPDATE/DELETE时,MySQL自动开始事务(隐式开始)
-- 相当于自动执行了 start transaction / begin

-- 执行commit / rollback 时,事务结束。

2.3 为什么需要setAutoCommit(false)

java 复制代码
// 如果不设置 setAutoCommit(false):
Connection conn = dataSource.getConnection(); // 默认 autocommit=true

// 每条SQL都会立即提交,无法回滚
conn.createStatement().executeUpdate("UPDATE ..."); // 立即提交!
conn.createStatement().executeUpdate("UPDATE ..."); // 立即提交!

// 如果第二条语句失败,第一条已经提交,无法回滚
// 原子性被破坏!

// 有了 setAutoCommit(false):
Connection conn = dataSource.getConnection();
conn.setAutoCommit(false); // 关闭自动提交

conn.createStatement().executeUpdate("UPDATE ..."); // 未提交
conn.createStatement().executeUpdate("UPDATE ..."); // 未提交

// 如果第二条失败,可以回滚第一条
conn.rollback(); // 事务回滚,两条都撤销

2.4 为什么Spring不直接使用START TRANSACTION?

Spring 不直接使用 START TRANSACTION 命令,而是通过 关闭自动提交 来实现事务,这是因为:

  • 兼容性:关闭自动提交是JDBC标准API,适用于所有数据库。
  • 灵活性:可以控制事务边界,而不仅仅是开始一个事务。
  • 性能:避免了额外的SQL语句开销。

3. Spring事务与MySQL事务的对应关系

复制代码
Spring事务生命周期           MySQL层面发生的事情
─────────────────────────────────────────────────────────────
@Transactional方法开始        
  ↓
DataSourceTransactionManager.doBegin()
  - 获取Connection
  - conn.setAutoCommit(false)  → MySQL: autocommit=0(仅设置)
  ↓
业务方法执行
  - 第一条SQL执行              → MySQL: 隐式 start transaction(事务真正开始)
  - 后续SQL执行                → MySQL: 在事务中执行
  ↓
方法正常结束
  - DataSourceTransactionManager.doCommit()
    - conn.commit()           → MySQL: COMMIT(提交事务)
  ↓
连接清理
  - 恢复autocommit=true       → MySQL: autocommit=1(恢复默认)
  - 连接归还连接池

4. Spring事务传播行为与连接绑定

java 复制代码
// 1. Spring通过ThreadLocal管理连接,实现传播行为
public abstract class TransactionSynchronizationManager {
    private static final ThreadLocal<Map<Object, Object>> resources =
            new NamedThreadLocal<>("Transactional resources");

    // 连接被绑定到当前线程
    public static void bindResource(Object key, Object value) {
        // ...
    }

    // 从当前线程获取连接
    public static Object getResource(Object key) {
        // ...
    }
}

// 2. 传播行为示例
@Component
@Transactional
public class AccountComponent {

	@Resource
	private AccountService accountService;

    public void methodA() {
        // 开启新事务,获取新连接,setAutoCommit(false)

        this.accountService.methodB(); // 会加入methodA的事务

        // 提交事务
    }
}

@Service
public class AccountService {

    @Transactional(propagation = Propagation.REQUIRED) // 默认事务传播行为
    public void methodB() {
        // 检查当前线程已有事务,加入已有事务
        // 不会执行setAutoCommit(false),因为已关闭自动提交
        // 使用methodA的连接
    }
}

@Service
public class TradeService {

    @Transactional(propagation = Propagation.REQUIRES_NEW) // 注意看这里的事务传播行为
    public void methodC() {
        // 即使当前线程已有事务,也开启新事务
        // 获取新连接,setAutoCommit(false)
        // 创建新的事务(MySQL层面的新事务)
    }
}

5. Spring事务的完整生命周期

java 复制代码
// Spring事务的完整控制流程
@Component
public class AccountService {

    @Transactional
    public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
        // 1. Spring AOP拦截方法调用
        // 2. 创建TransactionStatus
        // 3. DataSourceTransactionManager.doBegin()被调用

        // doBegin内部:
        // - 获取数据库连接
        // - setAutoCommit(false)  ← 这里!!!!!!
        // - 设置隔离级别
        // - 将连接绑定到ThreadLocal  ← 这里!!!!!!

        // 4. 执行业务方法
        accountRepository.decreaseBalance(fromId, amount);
        accountRepository.increaseBalance(toId, amount);

        // 5. 方法结束,如果没有异常:
        //    DataSourceTransactionManager.doCommit()被调用
        //    - 执行 connection.commit()

        // 6. 如果有异常:
        //    DataSourceTransactionManager.doRollback()被调用
        //    - 执行 connection.rollback()

        // 7. 清理:
        //    - 恢复连接的autoCommit状态(将其设置为true)  ← 这里!!!!!!
        //    - 从ThreadLocal解绑连接  ← 这里!!!!!!
        //    - 关闭连接(实际是还给连接池)
    }
}

三、总结与最佳实践

1. 核心要点总结

  • setAutoCommit(false) 不是开启MySQL事务的命令,而是 准备连接以支持事务
  • 真正的MySQL事务 在执行第一条SQL时隐式开始(或通过 start transaction 显式开始)。
  • Spring事务管理 是JDBC事务的增强封装,提供了声明式、编程式事务管理。
  • 连接与线程绑定 是Spring实现事务传播行为的关键机制。

2. 性能与注意事项

  • 连接获取成本:频繁开启事务会增加连接获取成本。
  • 事务粒度:尽量保持事务短小,避免长事务。
  • 自动提交恢复:Spring会正确恢复连接的自动提交状态,但自定义事务管理时需要注意。
  • 连接泄漏:确保事务结束后的连接正确释放。

通过深入理解Spring事务与MySQL事务的交互机制,我们可以更好地设计事务策略,避免常见的事务问题,并优化系统性能。

相关推荐
running up3 小时前
Spring IOC/DI 核心知识
java·spring·rpc
q_19132846953 小时前
基于SpringBoot2+Vue2的企业合作与活动管理平台
java·vue.js·经验分享·spring boot·笔记·mysql·计算机毕业设计
凌冰_3 小时前
JAVA与MySQL实现银行管理系统
java·开发语言·mysql
Han.miracle3 小时前
Spring WebMVC入门实战:从概念到连接建立全解析
java·spring boot·spring·springmvc
北友舰长4 小时前
基于Springboot+thymeleaf图书管理系统的设计与实现【Java毕业设计·安装调试·代码讲解】
java·spring boot·mysql·课程设计·图书管理·b/s·图书
麦麦鸡腿堡5 小时前
MySQL数据库操作指令
数据库·mysql
聪明努力的积极向上12 小时前
【MYSQL】字符串拼接和参数化sql语句区别
数据库·sql·mysql
2301_7683502312 小时前
MySQL为什么选择InnoDB作为存储引擎
java·数据库·mysql
哥哥还在IT中13 小时前
MySQL order by 如何优化
数据库·mysql