Spring 事务属性详解

Spring 事务属性详解

Spring 的 @Transactional 注解提供了一组属性,用来精确控制事务的行为。这些属性决定了事务如何传播、隔离级别、超时时间、是否只读,以及哪些异常触发回滚。

一、事务属性总览

属性 类型 默认值 作用
propagation Propagation REQUIRED 事务传播行为
isolation Isolation DEFAULT 事务隔离级别
timeout int -1(不超时) 事务超时时间(秒)
readOnly boolean false 是否只读事务
rollbackFor Class[] {} 触发回滚的异常类型
rollbackForClassName String[] {} 触发回滚的异常类名
noRollbackFor Class[] {} 不触发回滚的异常类型
noRollbackForClassName String[] {} 不触发回滚的异常类名

二、传播行为(propagation)

传播行为定义了事务方法被调用时,如何与当前已有事务交互。这是事务属性中最核心、最复杂的一个。

传播行为 说明 适用场景
REQUIRED 默认。当前有事务则加入,没有则新建 绝大多数业务方法
REQUIRES_NEW 挂起当前事务,新建独立事务 日志记录、审计操作
NESTED 当前有事务则嵌套执行(保存点),没有则新建 部分回滚场景
SUPPORTS 当前有事务则加入,没有则以非事务执行 查询方法
NOT_SUPPORTED 以非事务方式执行,挂起当前事务 不需要事务的操作
MANDATORY 必须在已有事务中执行,否则抛异常 强制要求事务的方法
NEVER 必须以非事务方式执行,否则抛异常 禁用事务的方法
java 复制代码
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder(Order order) {
    orderDao.save(order);
}

最常用的两个:

java 复制代码
// 主业务:加入或新建事务
@Transactional(propagation = Propagation.REQUIRED)
public void placeOrder(Order order) { }

// 日志:独立事务,即使主业务回滚,日志也要保留
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog(String message) { }

三、隔离级别(isolation)

隔离级别定义了事务之间的数据可见性,解决并发事务的脏读、不可重复读、幻读问题。

隔离级别 脏读 不可重复读 幻读
DEFAULT 使用数据库默认 --- ---
READ_UNCOMMITTED 可能 可能 可能
READ_COMMITTED 不会 可能 可能
REPEATABLE_READ 不会 不会 可能
SERIALIZABLE 不会 不会 不会
java 复制代码
@Transactional(isolation = Isolation.READ_COMMITTED)
public User getUser(Long id) {
    return userDao.findById(id);
}

注意: DEFAULT 表示使用底层数据库的默认隔离级别。MySQL InnoDB 默认是 REPEATABLE_READ,Oracle 默认是 READ_COMMITTED。

四、超时时间(timeout)

事务允许执行的最大时间,单位是秒。超时后事务自动回滚。

java 复制代码
@Transactional(timeout = 30)
public void batchProcess(List<Order> orders) {
    // 超过 30 秒自动回滚
}

默认值 -1 表示不超时,由数据库自己决定。超时时间不是精确的,实际超时可能略有偏差,因为检查时机由数据库或框架决定。

五、只读事务(readOnly)

标记事务为只读,用于优化查询性能。

java 复制代码
@Transactional(readOnly = true)
public List<User> findAll() {
    return userMapper.selectAll();
}

只读事务的作用:

  • 数据库可以做一些优化(如不记录回滚日志)。
  • 提示框架和数据库,这个事务不会修改数据。
  • 某些数据库驱动会对只读事务做特殊处理。

注意: readOnly = true 并不保证一定会阻止写操作。如果只读事务中执行了 INSERT、UPDATE、DELETE,行为取决于数据库实现,可能报错,也可能静默执行。

六、回滚规则

Spring 事务默认只在遇到 RuntimeException 和 Error 时回滚。检查型异常(Exception 的子类)不会触发回滚。

rollbackFor

指定哪些异常触发回滚。

java 复制代码
@Transactional(rollbackFor = {BusinessException.class, SQLException.class})
public void processOrder(Order order) throws BusinessException {
    // 抛出这些异常时回滚
}

rollbackForClassName

用类名字符串指定,效果与 rollbackFor 相同。

java 复制代码
@Transactional(rollbackForClassName = {"com.example.BusinessException"})

noRollbackFor

指定哪些异常不触发回滚。

java 复制代码
@Transactional(noRollbackFor = {BusinessException.class})
public void processOrder(Order order) throws BusinessException {
    // 抛出 BusinessException 时不回滚
}

noRollbackForClassName

用类名字符串指定不触发回滚的异常。

java 复制代码
@Transactional(noRollbackForClassName = {"com.example.BusinessException"})

最佳实践: 默认回滚规则只覆盖 RuntimeException。如果业务中会抛出检查型异常,务必显式指定 rollbackFor = Exception.class,避免异常被吞掉但数据已提交。

java 复制代码
@Transactional(rollbackFor = Exception.class)
public void createOrder(Order order) throws IOException {
    // 抛出 IOException 时也回滚
}

七、完整示例

java 复制代码
@Service
public class OrderService {
    
    @Autowired
    private OrderDao orderDao;
    
    @Autowired
    private InventoryDao inventoryDao;
    
    @Autowired
    private LogService logService;
    
    @Transactional(
        propagation = Propagation.REQUIRED,
        isolation = Isolation.READ_COMMITTED,
        timeout = 30,
        readOnly = false,
        rollbackFor = {BusinessException.class, SQLException.class},
        noRollbackFor = {NotifyException.class}
    )
    public void createOrder(Order order) throws BusinessException, SQLException {
        orderDao.save(order);
        inventoryDao.decrease(order.getProductId(), order.getQuantity());
        
        // 日志用独立事务,即使主业务回滚,日志也保留
        logService.saveLog("创建订单:" + order.getId());
    }
}

八、属性配置方式对比

配置方式 示例 适用场景
注解属性 @Transactional(propagation = ...) 方法或类级别
XML 配置 <tx:advice> + <aop:config> 老项目
编程式 TransactionTemplate 需要动态控制事务边界

九、常见问题

1. 默认回滚规则不覆盖检查型异常

java 复制代码
@Transactional
public void process() throws IOException {
    // IOException 是检查型异常,默认不回滚
    // 需要 rollbackFor = Exception.class
}

2. 传播行为选择不当导致数据不一致

java 复制代码
// ❌ 日志用 REQUIRED,主业务回滚时日志也回滚
@Transactional(propagation = Propagation.REQUIRED)
public void saveLog(String msg) { }

// ✅ 日志用 REQUIRES_NEW,主业务回滚日志仍保留
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog(String msg) { }

3. 只读事务中执行写操作

java 复制代码
@Transactional(readOnly = true)
public void updateUser(User user) {
    userDao.update(user);  // 可能报错,也可能静默执行
}

4. 超时时间设置过短

java 复制代码
@Transactional(timeout = 1)
public void batchProcess() {
    // 批量操作可能超过 1 秒,导致意外回滚
}

5. 隔离级别与数据库默认不一致

java 复制代码
// MySQL 默认 REPEATABLE_READ,显式设置为 READ_COMMITTED 可能改变行为
@Transactional(isolation = Isolation.READ_COMMITTED)

十、总结

属性 核心作用 默认值 常用值
propagation 事务传播行为 REQUIRED REQUIRED、REQUIRES_NEW
isolation 事务隔离级别 DEFAULT READ_COMMITTED、REPEATABLE_READ
timeout 超时时间(秒) -1 30
readOnly 是否只读 false 查询方法设 true
rollbackFor 触发回滚的异常 RuntimeException Exception.class
noRollbackFor 不触发回滚的异常 {} 业务异常

事务属性是 @Transactional 精确控制事务行为的核心。传播行为决定事务如何嵌套,隔离级别决定并发时的数据可见性,超时时间防止事务长期占用资源,只读标记优化查询性能,回滚规则决定哪些异常触发回滚。理解这些属性的含义和使用场景,才能写出正确、高效的事务代码。

相关推荐
FYKJ_201042 分钟前
express绿叶横店短剧推荐与影评分享平台34219-计算机课程设计、毕业设计
java·javascript·vue.js·spring boot·后端·课程设计·express
西凉的悲伤1 小时前
MyBatis 中 association 与 collection 详解
java·mybatis
我滴老baby1 小时前
Navidrome:飞牛OS安装、曲库导入与远程收听全流程
开发语言·数据库·php
code2cat1 小时前
Java进阶篇之AtomicLongArray:大数累加、上限检查与溢出边界
java·开发语言
开开心心_Every1 小时前
电脑OCR识别软件支持表格识别图片转Excel
java·开发语言·微信·计算机外设·ocr·intellij-idea·excel
弯_弯1 小时前
企业知识库 RAG 落地:从 Demo 到生产,四条工程链路与参数级自查
android·java·javascript
rannn_1111 小时前
JVM 垃圾回收面试题全解析:从基础到进阶
java·开发语言·jvm·后端·面试
Wang's Blog1 小时前
Java框架 SpringCloud 快速入门: Ribbon 饥饿加载
java·spring cloud·ribbon
瀚高PG实验室1 小时前
HGHAC集群VIP-MANAGER挂载的VIP消失
数据库·postgresql·瀚高数据库