本文目录导读:

理解Java事务传播行为的关键在于“内外事务方法是否共享同一个事务”,为了让你能清晰区分,下面通过几个典型场景的代码案例来分析。
核心概念:事务传播行为
Spring定义了7种传播行为,最常用来区分的是这3个:
| 传播行为 | 行为说明 |
|---|---|
REQUIRED(默认) |
支持当前事务,如果没有则新建 |
REQUIRES_NEW |
总是新建事务,如果已有则挂起 |
NESTED |
如果已有事务,则在嵌套事务中执行 |
案例场景设计
假设有一个银行系统,包含转账和日志记录两个操作,我们通过不同的传播行为来观察它们的行为差异。
案例1:REQUIRED vs REQUIRES_NEW
@Service
public class BankService {
@Autowired
private AccountDao accountDao;
@Autowired
private LogService logService;
@Transactional
public void transfer(String from, String to, int amount) {
// 扣减转出账户
accountDao.debit(from, amount);
// 记录转账日志(不同传播行为)
logService.recordLog(from, to, amount);
// 模拟异常:比如对转入账户余额不足
if (amount > accountDao.getBalance(to)) {
throw new RuntimeException("余额不足");
}
// 增加转入账户
accountDao.credit(to, amount);
}
}
@Service
public class LogService {
@Autowired
private LogDao logDao;
// 情况A:使用REQUIRED(默认)
@Transactional(propagation = Propagation.REQUIRED)
public void recordLog(String from, String to, int amount) {
logDao.insert(from + "->" + to + ":" + amount);
}
// 情况B:使用REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void recordLog(String from, String to, int amount) {
logDao.insert(from + "->" + to + ":" + amount);
}
}
运行结果对比
| 场景 | 转账方法异常时 | 日志是否回滚 | 最终数据状态 |
|---|---|---|---|
| REQUIRED | 抛出异常 | 回滚 | 转账未完成,日志不存在 |
| REQUIRES_NEW | 抛出异常 | 不回滚 | 转账未完成,日志已保存 |
关键区分点:REQUIRED内外事务绑定在一起,外层回滚内层也会回滚;REQUIRES_NEW内层事务独立提交。
案例2:REQUIRED vs NESTED
@Service
public class OrderService {
@Transactional
public void createOrder(Order order) {
// 保存订单主信息
orderDao.insert(order);
// 情况A:NESTED传播
this.saveOrderItems(order.getItems());
// 模拟异常
if (order.getTotal() > 10000) {
throw new RuntimeException("订单金额超限");
}
}
@Transactional(propagation = Propagation.NESTED)
public void saveOrderItems(List<Item> items) {
for (Item item : items) {
itemDao.insert(item);
}
}
}
运行结果对比
| 场景 | 异常点 | 订单主表 | 订单明细表 |
|---|---|---|---|
| 所有方法REQUIRED | 金额超限异常 | 回滚 | 回滚 |
| 子方法NESTED | 金额超限异常 | 回滚 | 回滚 |
| 子方法NESTED | 子方法内部异常 | 回滚 | 部分回滚(仅回滚子事务) |
关键区分点:
- NESTED允许子事务部分回滚,而REQUIRED不能
- 如果父事务最终回滚,NESTED子事务也会回滚(与REQUIRES_NEW不同)
案例3:调用链中的多种传播组合
@Service
public class PaymentService {
// 主事务
@Transactional(propagation = Propagation.REQUIRED)
public void processPayment(Payment payment) {
paymentDao.insert(payment);
// 发送通知(REQUIRES_NEW)
notificationService.sendNotification(payment);
// 记录审计日志(NESTED)
auditService.recordAudit(payment);
// 假设这里抛出异常
throw new RuntimeException("处理失败");
}
}
@Service
public class NotificationService {
// 独立事务,不受外部影响
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendNotification(Payment payment) {
notificationDao.insert(payment.getNotification());
}
}
@Service
public class AuditService {
// 嵌套事务
@Transactional(propagation = Propagation.NESTED)
public void recordAudit(Payment payment) {
auditLogDao.insert("Payment: " + payment.getId());
}
}
运行结果:
- 支付表:回滚
- 通知表:已提交(REQUIRES_NEW不受影响)
- 审计表:回滚(NESTED跟随外部事务回滚)
如何测试和验证
写一个单元测试来直观验证:
@RunWith(SpringRunner.class)
@SpringBootTest
public class TransactionPropagationTest {
@Autowired
private BankService bankService;
@Test
public void testRequiredPropagation() {
try {
bankService.transfer("A", "B", 1000);
} catch (Exception e) {
// 预期异常
}
// 验证:
// 1. 账户A余额不变 = REQUIRED时日志也回滚
// 2. 账户B余额不变 = 转账失败
// 3. 日志表中无记录 = 事务回滚
assertNull(logService.findLog("A->B:1000"));
}
@Test
public void testRequiresNewPropagation() {
try {
bankService.transfer("A", "B", 1000);
} catch (Exception e) {
// 预期异常
}
// 验证:
// 1. 账户A余额不变 = 转账失败
// 2. 日志表中依然有记录 = 独立事务已提交
assertNotNull(logService.findLog("A->B:1000"));
}
}
一个真实场景的判断技巧
当你需要判断使用哪种传播行为时,问自己这几个问题:
| 问题 | 对应传播行为 |
|---|---|
| 内层方法失败,外层是否继续? | NESTED |
| 内层方法失败,外层是否回滚? | REQUIRED(内层抛异常会传播) |
| 内层事务提交,是否想独立于外层? | REQUIRES_NEW |
| 内层方法需要感知外层事务的回滚? | REQUIRED |
| 内层方法不想被外层干扰? | NEVER 或 NOT_SUPPORTED |
一句话总结:
- 内外事务生死与共 →
REQUIRED - 内务独善其身 →
REQUIRES_NEW - 内务可部分回滚 →
NESTED
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播