【Spring】事务注解失效与传播机制

【Spring】事务注解失效与传播机制

Spring中的事务是通过@Transactional注解来实现的。

一、注解参数

@Transactional 注解的关键属性主要有如下:

重要 :@Transactional注解的方法不能在同一个类中直接内部调用,否则不走代理会失效。需要通过注入当前类的Bean对象进行调用。

1、isolation(隔离级别)

Isolation 枚举类中定义了五个表示隔离级别的值:

  • Isolation.DEFAULT:使用数据库默认的隔离级别【默认】
  • Isolation.READ_UNCOMMITTED:读取未提交数据(会出现脏读、不可重复读)
  • Isolation.READ_COMMITTED:读取已提交数据(会出现不可重复读和幻读)
  • Isolation.REPEATABLE_READ:可重复读(会出现幻读)
  • Isolation.SERIALIZABLE:串行化

注意:不同数据库的默认隔离级别可能不同(如MySQL默认是REPEATABLE_READ,PostgreSQL默认是READ_COMMITTED)。Spring的Isolation枚举是SQL-92标准的抽象,具体映射由各个数据库驱动实现。

2、propagation(传播行为)

@Transactional(propagation = Propagation.XXX) 主要用于控制方法被其他方法调用时的事务传播机制。

2.1 REQUIRED(默认)

如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

java 复制代码
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 操作数据库
    methodB(); // 调用另一个事务方法
}
 
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // 如果methodA有事务,methodB会加入它;否则自己新建事务
}
2.2 REQUIRES_NEW

总是创建一个新的事务,如果当前存在事务,则挂起当前事务。新建的事务完全独立,外层事务失败不会导致内层已提交的事务回滚。

java 复制代码
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 主事务操作
    try {
        methodB(); // 调用REQUIRES_NEW方法
    } catch (Exception e) {
        // 即使methodB失败,methodA可以继续
    }
}
 
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
    // 强制开启新事务,与methodA的事务独立
}
2.3 NESTED

如果当前存在事务,则在嵌套事务内执行(基于保存点Savepoint)。如果当前没有事务,则与REQUIRED行为相同。

嵌套事务的特点:

  • 内层事务(嵌套事务)失败不影响外层事务
  • 外层事务失败会导致内层嵌套事务一起回滚
  • 需要数据库支持保存点(MySQL的InnoDB不支持真正的嵌套事务)
java 复制代码
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 外层事务
    try {
        methodB(); // NESTED传播
    } catch (Exception e) {
        // methodB失败只回滚内层操作
    }
}
 
@Transactional(propagation = Propagation.NESTED)
public void methodB() {
    // 嵌套事务,有自己独立的保存点
}
2.4 SUPPORTS

如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。

2.5 MANDATORY

必须在事务中运行,如果当前没有事务,则抛出异常。

2.6 NOT_SUPPORTED

以非事务方式执行操作,如果当前存在事务,则挂起当前事务。挂起事务指暂停当前事务、保存现场、无事务执行、恢复现场的过程。

2.7 NEVER

必须在非事务中运行,如果当前存在事务,则抛出异常。

3、其他参数

3.1 rollbackFor / rollbackForClassName

指定哪些异常发生时需要回滚事务。

3.2 noRollbackFor / noRollbackForClassName

指定哪些异常发生时不需要回滚事务。
重要规则:

  1. Spring默认只对RuntimeException和Error进行事务回滚,Checked Exception默认不回滚
  2. 如果同时配置了rollbackFor和noRollbackFor且存在冲突,Spring采用"noRollbackFor优先"的原则
3.3 timeout

事务超时时间(秒),超过该时间事务未完成则自动回滚。

3.4 value / transactionManager

指定使用的事务管理器,用于多数据源场景。

二、事务失效的常见场景

1. 访问权限问题

@Transactional只能应用于public方法,private、protected、包访问权限的方法上注解无效。

2. 同一个类中的方法直接内部调用

解决方案:通过注入自身代理调用

selfProxy.methodB(); // ✅ 通过代理调用

3. 异常处理不当

  • 自己吞了异常:在方法中捕获异常但没有重新抛出
  • 抛出了错误的异常类型 :抛出的异常不在rollbackFor范围内且不是RuntimeException
java 复制代码
@Transactional
public void saveUser() {
    try {
        userDao.save(user);
    } catch (Exception e) {
        // ❌ 吞掉异常,事务不会回滚
        log.error("保存失败", e);
    }
}

4. 多线程调用

在开启新线程中执行数据库操作,事务不会跨线程传播。

5. 数据库引擎不支持事务

如MySQL使用MyISAM引擎(不支持事务),应使用InnoDB引擎。

三、编程式事务

Spring提供了编程式事务管理,可以更细粒度地控制事务边界:

java 复制代码
@Service
public class UserService {
    
    @Autowired
    private TransactionTemplate transactionTemplate;
    
    public void saveUser(final User user) {
        queryData1();
        queryData2();
        
        try {
            transactionTemplate.execute(status -> {
                addData1();
                updateData2();
                return Boolean.TRUE;
            });
        } catch (Exception e) {
            // 处理异常
            status.setRollbackOnly();
        }
    }
}

编程式事务 vs 声明式事务:

  • 声明式事务(@Transactional):简洁,基于AOP,适合大多数场景
  • 编程式事务:更灵活,可以精确控制事务边界,适合复杂业务逻辑

总结要点:

  1. 理解事务传播行为的区别,特别是REQUIRED、REQUIRES_NEW、NESTED
  2. 避免事务失效的常见陷阱,特别是自调用问题
  3. 合理配置异常回滚规则,理解冲突处理原则
  4. 根据业务需求选择声明式或编程式事务
相关推荐
斯内普吖9 小时前
(开源)宠物饲养系统实战指南 基于 Java + SSM + Vue + MySQL
java·vue.js·mysql·开源·宠物
墨家句子9 小时前
4G 显存老显卡怎么跑模型:llama.cpp 加 GGUF 配置
linux·后端
HLAIA光子9 小时前
RAG Chunks 切分优化后成本骤降 86%
后端·性能优化·架构
知守观9 小时前
Spring Boot 2.1 → 3.5 迁移推演:这个 2018 年的项目会炸在哪
java·spring boot
Flynt9 小时前
800KB 的 JSON 吃掉 8.5 秒 CPU:jackson 这波修复里,最容易被漏掉的洞藏在字符串里
java·安全
后端LV9 小时前
Caffeine 快到 1450 万 ops/s,为什么我还是不敢说数据一致
java·后端
蜗牛互联网9 小时前
Java 17调用Embeddings API:余弦相似度与FAQ拒答阈值
java·开发语言·人工智能
小小张说故事9 小时前
Python 装饰器把函数"搞丢"了?5 个必踩的坑与完整避坑对照表
后端·python
Q264336502310 小时前
【有源码】基于SpringBoot+Vue的博物馆展览与服务一体化平台-java博物馆数字化展品展示与交互平台设计
java·vue.js·spring boot·spring·毕业设计·javaweb·课程设计
guo_wen_qiang10 小时前
项目使用sentinel遇到的问题
java·sentinel