@Transactional 与 @Transactional(rollbackFor = Exception.class) 的区别详解

@Transactional 是 Spring 框架中用于声明事务的核心注解,而 @Transactional(rollbackFor = Exception.class) 是其带特定属性的使用方式。两者的主要区别在于事务回滚的触发条件不同,具体区别如下:

1. 默认行为(@Transactional)

当使用不带任何属性的 @Transactional 时,Spring 的默认事务默认只在遇到未检查异常(Unchecked Exception) 时才会触发回滚,具体包括:

  • 继承自 RuntimeException 的异常(如 NullPointerException、IllegalArgumentException 等)
  • 继承自 Error 的错误(如 OutOfMemoryError 等)

对于已检查异常(Checked Exception) (即直接继承自 Exception 且非 RuntimeException 的异常,如 IOException、SQLException 等),默认不会触发事务回滚,事务会继续提交。

2. @Transactional(rollbackFor = Exception.class) 的行为

当指定 rollbackFor = Exception.class 时,表示所有继承自 Exception 的异常(包括已检查异常和未检查异常)都会触发事务回滚,具体包括:

  • 未检查异常(RuntimeException 及其子类)
  • 已检查异常(如 IOException、SQLException 等)

这是一种更严格的回滚策略,确保任何异常(除了 Error 之外的 Throwable)都能触发回滚(注:Error 本身也会被默认回滚)。

3. 关键区别总结

注解形式 触发回滚的异常类型 不触发回滚的异常类型
@Transactional RuntimeException 及其子类、Error 及其子类 所有已检查异常(如 IOException、SQLException 等)
@Transactional(rollbackFor = Exception.class) 所有 Exception 及其子类(包括已检查和未检查异常)、Error 及其子类 无(几乎覆盖所有业务异常场景)

4. 何时使用哪种方式?

使用 @Transactional 的场景

适用于只需要在发生运行时异常(通常是程序逻辑错误)时回滚的场景,默认行为更轻量。

使用 @Transactional(rollbackFor = Exception.class) 的场景

适用于需要严格保证数据一致性的场景(如金融、订单等核心业务),确保任何异常(包括业务代码中主动抛出的已检查异常)都能触发回滚,避免数据不一致。

5. 实际开发建议

在实际开发中,推荐显式指定 rollbackFor = Exception.class ,因为业务逻辑中常自定义已检查异常(如 BusinessException),若不指定该属性,这些异常可能导致事务不回滚,引发数据问题。

6. 代码示例对比

默认 @Transactional 示例

java 复制代码
@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Transactional  // 默认只对RuntimeException回滚
    public void updateUserAndLog(Long userId, String newName) throws IOException {
        // 1. 更新用户信息
        User user = userRepository.findById(userId).orElseThrow();
        user.setName(newName);
        userRepository.save(user);
        
        // 2. 记录日志(假设这里可能抛出IOException)
        if (newName.length() > 50) {
            throw new IOException("用户名过长,日志记录失败");  // 已检查异常,默认不回滚
        }
        // 记录日志的逻辑...
    }
}

结果 :如果抛出 IOException,用户信息已更新但日志未记录,事务不会回滚,导致数据不一致。

@Transactional(rollbackFor = Exception.class) 示例

java 复制代码
@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Transactional(rollbackFor = Exception.class)  // 所有Exception都会回滚
    public void updateUserAndLog(Long userId, String newName) throws IOException {
        // 1. 更新用户信息
        User user = userRepository.findById(userId).orElseThrow();
        user.setName(newName);
        userRepository.save(user);
        
        // 2. 记录日志(假设这里可能抛出IOException)
        if (newName.length() > 50) {
            throw new IOException("用户名过长,日志记录失败");  // 已检查异常,会触发回滚
        }
        // 记录日志的逻辑...
    }
}

结果 :如果抛出 IOException,用户信息更新和日志记录都会回滚,保证数据一致性。

相关推荐
Irene199112 分钟前
Flink 学习日志 01:前端转大数据的实战避坑指南
java·jvm·flink
Thomas.Sir13 分钟前
第1课:SpringBoot 4.x 快速上手:新特性、环境准备与第一个HelloWorld程序
java·spring boot
devilnumber33 分钟前
Java 内部迭代 vs 外部迭代:从原理到实战避坑
java·开发语言
杨运交38 分钟前
080-缓存模块-Spring Cache缓存键前缀设计与租户隔离实践
java·spring·缓存
\光辉岁月/42 分钟前
5.math类
java·开发语言
文丑颜不良啊44 分钟前
LeetCode76~100题目解析
java·leetcode
她的男孩1 小时前
页面已发布用户仍进不去:Forge Admin 门户入口与系统菜单挂载解耦
java·spring boot·低代码
不像程序员的程序媛1 小时前
mybatisplus insert dto未指定id时会如何
java·开发语言
我命由我123451 小时前
5-2.Python 编码进阶 - 字符串进阶(拼接、普通格式化、普通格式化数字精度控制、快速格式化、遍历、常用方法)
java·开发语言·后端·python·学习·java-ee·学习方法
Pniubi9 小时前
HashMap的扩容和树化边界
java·开发语言