# 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
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
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
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
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
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
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
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
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
# 注意事项
- NESTED的限制:不是所有数据库都支持,MySQL InnoDB支持,Oracle支持
- REQUIRES_NEW的开销:新建事务有性能开销,频繁使用需注意
- 异常处理:不同传播行为的异常传播机制不同
- 测试验证:复杂的事务传播需要充分测试
- 文档记录:团队中明确各种传播行为的使用规范
理解并正确使用这些传播行为,可以帮助你设计出更健壮、更高效的事务处理逻辑。