1. @Transactional 的底层原理?

基于 AOP 动态代理:Spring 通过 TransactionInterceptor 拦截目标方法 → 方法执行前调用 PlatformTransactionManager.getTransaction()(内部 → AbstractPlatformTransactionManager.startTransaction() → DataSourceTransactionManager.doBegin():获取连接、关闭自动提交 autocommit=false、开启事务)→ 方法正常返回则 commit(),抛异常则 rollback() → finally 恢复连接并归还连接池。

事务三要素:

🎯 面试要点

  • 事务 = AOP + ThreadLocal 连接绑定:两个机制缺一不可,这是源码级理解的核心
  • MyBatis 的 SqlSession 也要参与绑定:所以 Spring 集成 MyBatis 时事务要由 Spring 管理(SqlSessionTemplate 内部代理)
  • 默认回滚规则:只回滚 RuntimeException 和 Error;受检异常不回滚(见失效场景)

2. 事务的传播行为(Propagation)?

传播行为:事务方法被另一个事务方法调用时,如何处理。七种,前三种常用:

经典场景:记录失败日志不污染主事务
@Service
class OrderService {
    @Transactional
    public void createOrder() {
        try {
            // 主流程...
        } catch (Exception e) {
            logService.saveLog(e);   // REQUIRES_NEW:独立事务,主事务回滚不影响日志入库
            throw e;
        }
    }
}

🎯 面试要点

  • REQUIRES_NEW 注意:外层事务挂起期间数据库连接可能被占用更长,注意连接池大小
  • NESTED 与 REQUIRES_NEW 的区别:REQUIRES_NEW 在新连接上开一个彻底独立的事务,内层提交后即使外层回滚也依然有效;NESTED 是同一事务内的保存点,内层回滚只回到保存点、外层可继续,但外层一旦回滚内层也会一起回滚
  • 事务方法必须走代理调用才生效(自调用问题,见 AOP 页)

3. @Transactional 失效的场景有哪些?(必考,背全)

  1. 方法自调用(this 调用):不走代理 → 无事务。最常考
  2. 非 public 方法:Spring 只代理 public(源码检查,private/protected 直接不处理)
  3. 异常被 catch 吞掉:事务拦截器没看到异常 → 不回滚。必须在 catch 中抛出或手动 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
  4. 抛出受检异常:默认只回滚 RuntimeException/Error。受检异常要 rollbackFor = Exception.class
  5. 类未被 Spring 管理:没加 @Service/@Component 等,或者类被 final 修饰(CGLIB 无法代理)
  6. 数据库引擎不支持事务:MyISAM 表无事务(要用 InnoDB)
  7. 多线程调用:新线程的 TransactionSynchronizationManager 是独立的,事务上下文传不过去 → 新线程中的操作不受事务保护
  8. 事务传播配置不当:外层 NOT_SUPPORTED 等
最经典的两种
// ❌ 失效 1:异常被吞
@Transactional
public void save() {
    try { dao.insert(); } catch (Exception e) { log.error(e); }
    // 异常没抛出去 → 拦截器不知道 → 事务照常提交
}

// ❌ 失效 2:受检异常默认不回滚
@Transactional
public void save() throws IOException {
    dao.insert();
    throw new IOException("...");   // 不回滚!
}
// ✅ 修正:@Transactional(rollbackFor = Exception.class)

🎯 面试要点

  • rollbackFor 一定要写:@Transactional(rollbackFor = Exception.class) 是阿里规约强制要求
  • 多线程 + 事务:事务传播不了线程,需要其他方案(如本地消息表/事务消息)
  • 验证事务是否生效:看日志中的 "Creating new transaction" / 或者查询 @Transactional 方法代理状态

4. 事务隔离级别与并发问题?

@Transactional(isolation = ...),对应数据库隔离级别(详见 MySQL 模块):

数据库并发三问题:脏读(读到未提交)、不可重复读(同事务两次读同一行结果不同)、幻读(同事务两次查询结果集数量不同)。

🎯 面试要点

  • Spring 的 isolation 默认取数据库默认(DEFAULT)
  • 只读优化:readOnly=true 只对"全只读"方法用,可减少锁开销(部分数据库有优化)
  • 隔离级别设置要数据库配合(MySQL 在 InnoDB 下才有效)

5. 编程式事务 vs 声明式事务?

TransactionTemplate 用法
@Autowired private TransactionTemplate txTemplate;

public void doBiz() {
    txTemplate.execute(status -> {
        try {
            daoA.insert(...);
            daoB.update(...);
            return null;                 // 正常 → 提交
        } catch (Exception e) {
            status.setRollbackOnly();         // 标记回滚
            throw e;
        }
    });
}

🎯 面试要点

  • TransactionTemplate 默认也是只回滚 RuntimeException,注意同样配置 rollbackFor 语义(自定义 TransactionAttribute)
  • 事务管理器的选择:多数据源时每个数据源一个 DataSourceTransactionManager,事务只能覆盖单个数据源(跨库要分布式事务,见分布式模块)

🎤 常见面试追问

  1. @Transactional 失效场景有哪些?(背全)——自调用、非 public 方法、异常被 catch 吞掉、抛受检异常(默认不回滚)、类没被 Spring 管理、final 类/方法(CGLIB 无法代理)、多线程调用(事务上下文不过线程)。
  2. 事务和数据库连接是什么关系?——事务期间的数据库连接存在 ThreadLocal 里,同一线程的 DAO 操作共用同一连接(否则各拿各的连接事务无效)——这是事务生效的底层机制。
  3. REQUIRES_NEW 和 NESTED 的区别?——REQUIRES_NEW:挂起当前事务开全新事务,内层回滚不影响外层已做;NESTED:保存点回滚,外层可以 catch 继续。
  4. 为什么异常被吞事务就不回滚?——事务拦截器靠"方法抛出异常"来感知失败并回滚;catch 住不抛,拦截器以为成功了,照常提交。
  5. 多数据源事务怎么处理?——Spring 事务只能管一个数据源(一个 DataSourceTransactionManager 管一个库);跨库需要分布式事务(TCC/Saga/Seata,见分布式模块)。

📖 名词解释(本页术语)

术语 大白话解释
事务一组操作要么全成功要么全回滚(ACID)。转账扣钱+加钱必须在一个事务里。
@Transactional声明式事务注解:标注的方法自动纳入事务管理(AOP 实现)。建议写 rollbackFor = Exception.class。
传播行为(Propagation)事务方法被另一个事务方法调用时的处理规则:REQUIRED(默认,加入或新建)、REQUIRES_NEW(开新的)、NESTED(保存点)等 7 种。
隔离级别(Isolation)并发事务的隔离程度:读未提交/读已提交/可重复读/串行化(MySQL 默认可重复读,详见 MySQL 模块)。
回滚(Rollback)把事务内已做的修改撤销。Spring 默认只对 RuntimeException/Error 回滚。
DataSourceTransactionManagerJDBC 事务管理器:负责 begin/commit/rollback,并把连接绑定到当前线程(ThreadLocal)。
TransactionTemplate编程式事务:方法内自己控制事务范围(粒度到代码块),适合部分逻辑需要事务的场景。
⚠️ 本页面由 AI 生成,内容仅供参考,请以官方文档和实际源码为准。