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

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

1. 默认行为(@Transactional

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

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

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

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

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

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

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

3. 关键区别总结

注解形式 触发回滚的异常类型 不触发回滚的异常类型
@Transactional RuntimeException 及其子类、Error 及其子类 所有已检查异常(如 IOExceptionSQLException 等)
@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,用户信息更新和日志记录都会回滚,保证数据一致性。

相关推荐
西门吹-禅1 小时前
【eclipse 升级】
java·ide·eclipse
Seven971 小时前
剑指offer-78、求平⽅根
java
玄〤1 小时前
个人博客网站搭建day6--Spring Boot自定义RedisTemplate配置:优化序列化与Java8时间类型支持
java·spring boot·redis·后端·spring
敲敲千反田2 小时前
CAS和AQS相关问题
java
上海合宙LuatOS2 小时前
LuatOS核心库API——【iotauth 】 IOT 鉴权库
java·单片机·嵌入式硬件·物联网·struts·计算机外设·硬件工程
luod2 小时前
Docker 快速安装Jenkins
java·docker·jenkins
三水不滴2 小时前
利用SpringCloud Gateway 重试 + 降级解决第三方接口频繁超时问题,提升性能
经验分享·笔记·后端·spring·spring cloud·gateway
senijusene2 小时前
Linux软件编程: 线程属性与线程间通信详解
java·linux·jvm·算法
昱宸星光2 小时前
spring cloud gateway内置路由断言工厂
java·开发语言·前端