# Spring事务传播行为详解

# 1. REQUIRED(默认)

@Transactional(propagation = Propagation.REQUIRED)
1

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

典型场景

@Service
public class OrderService {
    @Transactional(propagation = Propagation.REQUIRED)
    public void placeOrder(Order order) {
        // 订单核心业务逻辑
        orderDao.save(order);
        inventoryService.deductStock(order);  // 调用其他服务方法
        paymentService.processPayment(order); // 多个操作在同一个事务中
    }
}
1
2
3
4
5
6
7
8
9
10

# 2. REQUIRES_NEW

@Transactional(propagation = Propagation.REQUIRES_NEW)
1

作用:总是创建一个新的事务,如果当前存在事务,则挂起当前事务。

典型场景

@Service
public class AuditService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logOperation(String operation, String userId) {
        // 审计日志 - 必须独立保存,即使主业务失败
        auditLogDao.save(new AuditLog(operation, userId));
    }
}

@Service
public class OrderService {
    @Transactional
    public void placeOrder(Order order) {
        orderDao.save(order);
        
        // 审计日志需要独立事务
        auditService.logOperation("CREATE_ORDER", order.getUserId());
        // 即使这里抛出异常,审计日志已经提交
        
        inventoryService.deductStock(order); // 可能失败,但日志已保存
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 3. NESTED

@Transactional(propagation = Propagation.NESTED)
1

作用:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则创建一个新的事务。

特点

  • 嵌套事务是外部事务的一部分
  • 嵌套事务可以独立回滚,不影响外部事务
  • 外部事务回滚,嵌套事务一定回滚
  • 需要数据库支持保存点(如MySQL的InnoDB)

典型场景

@Service
public class BatchImportService {
    @Transactional
    public void batchImport(List<Product> products) {
        for (Product product : products) {
            try {
                // 每个产品的导入是嵌套事务
                importSingleProduct(product);
            } catch (ProductImportException e) {
                // 单个产品导入失败,记录日志继续导入其他产品
                log.error("产品导入失败: {}", product.getId(), e);
                // 嵌套事务回滚,但外部事务继续
            }
        }
    }
    
    @Transactional(propagation = Propagation.NESTED)
    public void importSingleProduct(Product product) {
        productDao.save(product);
        // 校验库存
        validateStock(product);
        // 生成商品编码
        generateProductCode(product);
        // 如果这里失败,只回滚当前产品的操作
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 4. SUPPORTS

@Transactional(propagation = Propagation.SUPPORTS)
1

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

典型场景

@Service
public class ReportService {
    @Transactional(propagation = Propagation.SUPPORTS)
    public Report generateReport(DateRange range) {
        // 查询报表数据
        List<Order> orders = orderDao.findByDateRange(range);
        List<Payment> payments = paymentDao.findByDateRange(range);
        
        // 生成报表(只读操作)
        return ReportBuilder.build(orders, payments);
        
        // 可以在事务中调用(复用事务),也可以单独调用(无事务)
    }
}

// 使用方式1:在事务中调用
@Transactional
public void processAndReport() {
    processOrders();
    reportService.generateReport(range); // 复用当前事务
}

// 使用方式2:单独调用
public void generateReportOnly() {
    reportService.generateReport(range); // 无事务执行
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 5. NOT_SUPPORTED

@Transactional(propagation = Propagation.NOT_SUPPORTED)
1

作用:以非事务方式执行操作,如果当前存在事务,则挂起当前事务。

典型场景

@Service
public class FileService {
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void exportLargeDataToFile(String filePath) {
        // 大量数据导出到文件 - 耗时操作,不需要事务
        List<Data> dataList = dataDao.findAll(); // 百万级数据
        
        try (FileWriter writer = new FileWriter(filePath)) {
            for (Data data : dataList) {
                writer.write(data.toCsv());
                // 耗时操作,长时间占用连接
            }
        }
        // 如果使用事务,连接会长时间不释放
    }
}

@Service 
public class DataService {
    @Transactional
    public void processAndExport() {
        // 数据处理(需要事务)
        processData();
        
        // 文件导出(不需要事务,挂起当前事务)
        fileService.exportLargeDataToFile("/export/data.csv");
        
        // 继续其他需要事务的操作
        updateProcessStatus();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 6. NEVER

@Transactional(propagation = Propagation.NEVER)
1

作用:以非事务方式执行,如果当前存在事务,则抛出异常。

典型场景

@Service
public class HealthCheckService {
    @Transactional(propagation = Propagation.NEVER)
    public HealthStatus checkSystemHealth() {
        // 健康检查 - 必须确保不在事务中执行
        // 避免事务锁影响检查结果
        
        checkDatabaseConnection();
        checkCacheConnection();
        checkExternalService();
        
        return healthStatus;
        
        // 如果被事务方法调用,会抛出异常:
        // IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
    }
}

// 正确使用
public void monitorSystem() {
    // 独立调用,不在事务中
    healthService.checkSystemHealth();
}

// 错误使用
@Transactional
public void wrongUsage() {
    processData();
    healthService.checkSystemHealth(); // 抛出异常!
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 7. MANDATORY

@Transactional(propagation = Propagation.MANDATORY)
1

作用:必须在事务中调用,如果当前没有事务,则抛出异常。

典型场景

@Service
public class AuditTrailService {
    @Transactional(propagation = Propagation.MANDATORY)
    public void recordAuditTrail(AuditEvent event) {
        // 审计追踪 - 必须在事务中记录
        // 确保与业务操作原子性
        
        auditDao.save(event);
        // 必须与主业务在同一个事务中提交或回滚
    }
}

@Service
public class OrderService {
    @Transactional  // 必须有事务
    public void updateOrder(Order order) {
        orderDao.update(order);
        
        // 正确:在事务中调用
        auditTrailService.recordAuditTrail(
            new AuditEvent("UPDATE_ORDER", order.getId())
        );
    }
    
    public void viewOrder(Long orderId) {
        // 错误:不在事务中调用
        // auditTrailService.recordAuditTrail(...); // 抛出异常!
        
        orderDao.findById(orderId); // 只读操作,不需要事务
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 传播行为对比表

传播行为 当前有事务 当前无事务 典型场景
REQUIRED 加入事务 新建事务 默认选择,大多数业务方法
REQUIRES_NEW 新建事务,挂起当前 新建事务 独立操作(日志、通知)
NESTED 嵌套事务(保存点) 新建事务 批量处理中的子操作
SUPPORTS 加入事务 无事务运行 可事务可非事务的查询
NOT_SUPPORTED 挂起事务,无事务运行 无事务运行 耗时操作,避免长事务
NEVER 抛出异常 无事务运行 强制不在事务中执行
MANDATORY 加入事务 抛出异常 强制在事务中执行

# 实际项目中的选择建议

java

@Service
public class ComprehensiveExample {
    
    // 1. 主业务方法 - REQUIRED(默认)
    @Transactional
    public void placeOrder(Order order) {
        // 核心业务逻辑
        orderDao.save(order);
        
        // 2. 需要独立保存的日志 - REQUIRES_NEW
        auditService.logOperation("PLACE_ORDER", order.getId());
        
        // 3. 批量处理子项 - NESTED(如果支持)
        for (Item item : order.getItems()) {
            try {
                itemService.processItem(item);  // NESTED传播
            } catch (Exception e) {
                // 单个失败不影响整体
                log.warn("Item process failed: {}", item.getId());
            }
        }
        
        // 4. 发送通知 - NOT_SUPPORTED
        notificationService.sendEmail(order);  // 避免事务锁
        
        // 5. 必须在事务中的操作 - MANDATORY
        transactionService.recordTransaction(order);  // 强制事务
    }
}

@Component
class NotificationService {
    // 邮件发送不需要事务
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void sendEmail(Order order) {
        // 可能调用外部API,耗时不固定
        emailClient.send(order.getUserEmail(), "订单确认");
    }
}

@Component  
class TransactionService {
    // 交易记录必须与订单在同一个事务
    @Transactional(propagation = Propagation.MANDATORY)
    public void recordTransaction(Order order) {
        transactionDao.save(Transaction.fromOrder(order));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

# 注意事项

  1. NESTED的限制:不是所有数据库都支持,MySQL InnoDB支持,Oracle支持
  2. REQUIRES_NEW的开销:新建事务有性能开销,频繁使用需注意
  3. 异常处理:不同传播行为的异常传播机制不同
  4. 测试验证:复杂的事务传播需要充分测试
  5. 文档记录:团队中明确各种传播行为的使用规范

理解并正确使用这些传播行为,可以帮助你设计出更健壮、更高效的事务处理逻辑。

Last Updated: 12/4/2025, 10:23:47 AM