Spring事务详解

1 阅读6分钟

一、事务基础概念

1. 事务四大特性 ACID

  1. 原子性 Atomic:一组操作要么全成功,要么全失败回滚,不可分割
  2. 一致性 Consistency:事务前后数据完整性约束不被破坏(主键、外键、唯一索引)
  3. 隔离性 Isolation:多个事务并发执行互不干扰,通过隔离级别控制脏读、不可重复读、幻读
  4. 持久性 Durability:事务提交后数据永久落库,宕机不丢失

2. 原生 JDBC 事务痛点

Connection conn = DriverManager.getConnection();
conn.setAutoCommit(false); // 关闭自动提交
try {
    // DML操作
    conn.commit();
} catch(Exception e) {
    conn.rollback();
} finally {
    conn.close();
}

问题:

  • 重复模板代码,耦合业务
  • 多数据源、多框架(MyBatis/JdbcTemplate)事务统一管理复杂
  • 无法实现声明式事务,所有事务逻辑写死在代码

3. Spring 事务解决思路

统一抽象事务 API + AOP 声明式事务

  1. 抽象事务接口,屏蔽 JDBC/MyBatis/JPA 底层差异
  2. AOP 环绕通知自动拦截业务方法,自动开启、提交、回滚事务
  3. 两种使用方式:编程式事务、声明式事务(主流)

二、Spring 事务核心三大顶层接口

1. PlatformTransactionManager 事务管理器(核心)

统一事务操作规范,不同持久层提供不同实现,第三方数据源必须配置该 Bean

public interface PlatformTransactionManager {
    // 获取事务状态(开启事务)
    TransactionStatus getTransaction(TransactionDefinition definition);
    // 提交事务
    void commit(TransactionStatus status);
    // 回滚事务
    void rollback(TransactionStatus status);
}

常用实现类:

  1. DataSourceTransactionManager:MyBatis、JdbcTemplate、Druid/Hikari 关系型数据库(最常用)
  2. JpaTransactionManager:JPA/Hibernate
  3. RabbitTransactionManager:RabbitMQ 消息事务
  4. RedisTransactionManager:Redis 事务

2. TransactionDefinition 事务定义

封装事务所有配置属性:传播行为、隔离级别、超时时间、只读、回滚规则

3. TransactionStatus 事务状态

记录当前事务运行状态:是否新事务、是否已提交、是否回滚、是否有保存点


三、五大核心配置(@Transactional 注解全部对应)

1. 事务传播行为 propagation(重点,多方法嵌套事务)

控制方法嵌套调用时事务如何传递,7 种类型:

表格

传播类型说明
REQUIRED(默认)有事务就加入,无则新建事务
REQUIRES_NEW无论有无外层事务,新建独立事务,外层回滚不影响内层
SUPPORTS有事务就运行在事务,无事务则非事务运行
NOT_SUPPORTED强制挂起当前事务,以非事务执行
MANDATORY必须存在外层事务,否则抛异常
NEVER禁止存在事务,有事务直接报错
NESTED嵌套事务,基于保存点;外层回滚内层一起回滚,内层回滚不影响外层

2. 事务隔离级别 isolation(解决并发脏读 / 不可重复读 / 幻读)

底层依赖数据库支持,5 种级别:

  1. DEFAULT:使用数据库默认隔离级别(MySQL InnoDB 默认 REPEATABLE READ)
  2. READ_UNCOMMITTED:读未提交,存在脏读、不可重复读、幻读
  3. READ_COMMITTED:读已提交,解决脏读,存在不可重复读、幻读(Oracle 默认)
  4. REPEATABLE_READ:可重复读,解决脏读、不可重复读,存在幻读(MySQL 默认)
  5. SERIALIZABLE:串行化,全部问题解决,性能最低,锁表

并发问题说明

  • 脏读:事务 A 读到事务 B 未提交的数据,B 回滚导致 A 读到脏数据
  • 不可重复读:同一事务内,两次查询同一数据,中间被其他事务修改提交,两次结果不一致
  • 幻读:同一事务两次范围查询,中间其他事务插入新数据,第二次查询多出行

3. rollbackFor /noRollbackFor 回滚规则

Spring 默认:仅抛出 RuntimeException、Error 才自动回滚

  • rollbackFor = Exception.class:所有异常(包括受检 Exception)都回滚
  • noRollbackFor = BusinessException.class:抛出该异常不回滚

4. timeout 事务超时时间

单位秒,事务执行超过时间自动回滚,默认 - 1 无限制

5. readOnly 只读事务

标记查询方法,数据库优化(不开启写锁),写操作会抛异常


四、两种事务使用方式

方式 1:编程式事务(极少使用)

手动编码控制事务,灵活性高,代码侵入强

方式 A:TransactionTemplate(推荐编程式)

@Service
public class OrderService {
    @Autowired
    private TransactionTemplate transactionTemplate;

    public void createOrder() {
        transactionTemplate.execute(status -> {
            // 业务操作
            orderMapper.insert();
            stockMapper.deduct();
            // 抛异常自动回滚
            return null;
        });
    }
}

方式 B:原生 PlatformTransactionManager

手动开启、提交、回滚,代码冗余

方式 2:声明式事务(企业开发主流,AOP 实现)

1. 注解 @Transactional(最常用)

使用规则
  1. 加在 public 方法上(AOP 动态代理只能拦截 public)
  2. 推荐写在 Service 层业务方法 / 类上
  3. 底层:Spring AOP 环绕通知,方法执行前开启事务,正常结束提交,异常回滚
@Service
public class OrderServiceImpl implements OrderService {

    // 完整配置示例
    @Transactional(
            propagation = Propagation.REQUIRED,
            isolation = Isolation.DEFAULT,
            timeout = 30,
            readOnly = false,
            rollbackFor = Exception.class
    )
    @Override
    public void placeOrder() {
        // 扣库存、创建订单、记录流水
    }
}

2. XML 声明式事务(老 SSM 项目)

<!-- 1.事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!-- 2.事务通知 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="add*" propagation="REQUIRED" rollback-for="Exception"/>
        <tx:method name="update*" propagation="REQUIRED"/>
        <tx:method name="select*" read-only="true"/>
    </tx:attributes>
</tx:advice>
<!-- 3.AOP切面拦截Service所有方法 -->
<aop:config>
    <aop:pointcut expression="execution(* com.service.*.*(..))" id="servicePoint"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="servicePoint"/>
</aop:config>

3. 开启注解事务支持

原生 Spring JavaConfig
@Configuration
@ComponentScan("com")
@EnableTransactionManagement // 开启事务注解驱动
public class SpringConfig {
    // 必须注册事务管理器Bean
    @Bean
    public PlatformTransactionManager txManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}
SpringBoot

自动开启 @EnableTransactionManagement,只需注入数据源,自动装配 DataSourceTransactionManager,直接使用 @Transactional


五、Spring 事务底层 AOP 执行流程

  1. IOC 创建 Service 代理对象(JDK 动态代理 / CGLIB)
  2. 调用业务方法时,进入事务拦截器 TransactionInterceptor
  3. 拦截器获取 @Transactional 配置封装 TransactionDefinition
  4. 调用 txManager.getTransaction() 开启事务,关闭连接自动提交
  5. 执行目标业务方法
  6. 方法正常执行无异常:txManager.commit ()
  7. 方法抛出指定异常:txManager.rollback ()
  8. 释放数据库连接

六、高频事务失效场景(面试重点)

  1. 方法非 public @Transactional 写在 private/protected 方法,AOP 无法拦截,事务失效

  2. 同类内部方法自调用

    @Service
    public class OrderService {
        public void a() {
            this.b(); // 直接this调用,不走代理,b的事务失效
        }
        @Transactional
        public void b() {}
    }
    

    解决:通过容器代理对象调用 ((OrderService)AopContext.currentProxy()).b()

  3. 异常类型不匹配 抛出受检 Exception,未配置 rollbackFor = Exception.class,不回滚

  4. 异常被 try-catch 捕获未抛出

    @Transactional
    public void test() {
        try {
            mapper.insert();
            int i = 1/0;
        } catch (Exception e) {
            // 异常吞掉,AOP感知不到异常,不会回滚
        }
    }
    

    解决:catch 后手动抛出新异常

  5. 多数据源未指定对应事务管理器 多库场景,未通过 @Transactional(transactionManager = "tx1") 指定管理器

  6. 传播行为配置错误 如外层 REQUIRED、内层 NOT_SUPPORTED,内层操作无事务,异常不回滚

  7. 数据库引擎不支持事务 MySQL MyISAM 不支持事务,必须使用 InnoDB


七、多数据源事务简单配置

多个数据库需要配置多个 DataSourceTransactionManager,使用时指定管理器名称

@Bean("tx1")
public PlatformTransactionManager tx1(DataSource ds1) {
    return new DataSourceTransactionManager(ds1);
}

@Bean("tx2")
public PlatformTransactionManager tx2(DataSource ds2) {
    return new DataSourceTransactionManager(ds2);
}

使用:

@Transactional(transactionManager = "tx1", rollbackFor = Exception.class)

八、核心面试总结

  1. Spring 事务分为编程式、声明式,底层基于 AOP 动态代理
  2. 三大核心接口:PlatformTransactionManager(管理器)、TransactionDefinition(配置)、TransactionStatus(状态)
  3. @Transactional 核心属性:传播行为、隔离级别、回滚规则、只读、超时
  4. 默认只对 RuntimeException / Error 回滚,受检异常需手动指定 rollbackFor
  5. 事务失效九大场景是高频考点,核心根源:AOP 代理未生效、异常未抛出、数据库不支持事务
  6. SpringBoot 自动装配事务管理器,原生 Spring 必须手动注册 DataSourceTransactionManager + @EnableTransactionManagement