@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,用户信息更新和日志记录都会回滚,保证数据一致性。

相关推荐
星辰_mya3 小时前
@SpringBootApplication 与 SPI 机制的终极解密
java·spring boot·spring
xdl25993 小时前
【异常解决】Unable to start embedded Tomcat Nacos 启动报错
java·tomcat
是2的10次方啊3 小时前
串行与并行:高并发系统里的优雅接口设计
java
qiuyuyiyang3 小时前
SpringBoot中如何手动开启事务
java·spring boot·spring
sheji34163 小时前
【开题答辩全过程】以 摩托车及配件售后管系统为例,包含答辩的问题和答案
java
我是苏苏3 小时前
消息中间件RabbitMQ04:路由模式+死信队列的应用实践模板
java·开发语言
花无缺0003 小时前
Java开发踩坑:一次线上性能优化案例
java·开发语言·人工智能·面试
yashuk3 小时前
SpringBoot中自定义Starter
java·spring boot·后端
一只大袋鼠3 小时前
并发编程(二十三):单例模式(二):静态/非静态方法:单例内存优化关键
java·单例模式·并发编程
6+h4 小时前
【java IO】缓冲流详解
java·开发语言