一、概念
事务传播描述一个方法A(可能在事务中),调用方法B时,方法B可能的几种情形(加入A的事务?自己新建事务?非事务?抛异常?)。
二、事务传播行为
解释一下情形,以及图解中的符号含义:
1. 方法A调用方法B,这是事务传播发生的条件,单个方法没有”传播“一说。
A是外部函数,B是内部函数,那么A调用完B还会回到A,我将这个过程表示成:
A -> B -> A。图解将会对这一过程做一些框线标记,以描述A和B都是在哪个事务中进行的。
2. B要根据A是否在事务中,以及是7种规则的哪一种,选择自己的行为——加入事务?新建事务?嵌套创建事务?非事务事务?抛异常?。
3. java给了7种规则,规定B的行为,每种规则都存在“A在事务中,A不在事务”中两种情形。
4. 图解解释:
方框代表事务,方框里面有A,代表A在事务中。
A和B在同一个方框内,代表二者在同一个事务中。
方框用线连起来代表还是同一个事务,需要用线连起来,说明该事务发生了挂起。
三角形感叹号代表抛异常。
| 传播行为 | 当前有事务 | 当前无事务 | 图解 |
|---|---|---|---|
| 1. REQUIRED (默认) | 加入当前事务 | 新建事务 | ![]() A在事务内,B则加入A的事务; A不在事务内,B自己新建事务; |
| 2.SUPPORTS | 加入当前事务 | 非事务运行 | ![]() A在事务内,B则加入A的事务; A不在事务内,B则非事务运行; |
| 3. MANDATORY | 加入当前事务 | 抛异常 |
A在事务内,B则加入A的事务; |
| 4. REQUIRES_NEW | 挂起当前,新建事务 | 新建事务 |
A在事务内,挂起A所在的事务,B则新建事务; |
| 5. NOT_SUPPORTED | 挂起当前,非事务运行 | 非事务运行 |
A在事务内,挂起A所在的事务,B则非事务运行; |
| 6. NEVER | 抛异常 | 非事务运行 |
A在事务内,抛异常; |
| 7. NESTED | 嵌套在当前事务中(Savepoint) | 新建事务 |
A在事务内,B在当前事务中建事务; |
三、使用场景
| 传播行为 | 英文枚举值 | 使用场景描述 | 典型业务场景示例 |
|---|---|---|---|
| REQUIRED (默认) | Propagation.REQUIRED | 需要在一个事务中运行。如果已经存在事务就加入,不存在则创建新事务。 | - 用户注册:需要同时向用户表插入数据、初始化用户配置表,这两个操作必须在同一事务中。 - 订单创建:扣减库存、生成订单、计算积分必须在同一事务中成功或回滚。 |
| SUPPORTS | Propagation.SUPPORTS | 支持当前事务,但不需要强事务保证。在事务中则用事务,不在事务中就非事务执行。 | - 商品信息查询:在修改商品信息的业务流中调用查询,会加入事务保证读到最新数据;但前台直接查询商品详情时,不需要事务。 - 数据报表统计:对数据一致性要求不高的统计查询。 |
| MANDATORY | Propagation.MANDATORY | 强制必须在已有事务中运行。如果没有活动事务则抛出异常,用于防止误调用。 | - 资金结算:该操作绝对不能在没有事务保护的情况下单独运行,必须由上层事务方法调用。 - 核心业务步骤:某个步骤是更大业务流程的一部分,不允许独立执行。 |
| REQUIRES_NEW | Propagation.REQUIRES_NEW | 总是开启一个独立的新事务。暂停当前事务(如果存在),新事务与原有事务完全独立。 | - 操作日志记录:无论主业务(如转账)成功还是失败,操作日志都必须被记录,不能回滚。 - 审计信息写入:业务操作的审计跟踪需要独立持久化。 - 通知第三方系统:调用外部API发送通知,不希望因为外部系统响应慢或失败而回滚主业务。 |
| NOT_SUPPORTED | Propagation.NOT_SUPPORTED | 明确不在事务中运行。如果存在当前事务,则挂起它,以非事务方式执行。 | - 发送短信/邮件:这些操作本身不具备事务性,且不希望因它们失败或耗时较长而拖累主数据库事务。 - 调用非事务性存储:向Redis、Elasticsearch等非关系型数据库写入数据。 |
| NEVER | Propagation.NEVER | 坚决不在事务中运行。如果检测到当前存在事务,则直接抛出异常。 | - 性能敏感查询:执行非常耗时的数据查询,明确要求不能有任何事务开销,如果被意外地在事务中调用,则快速失败。 - 数据缓存预热:从数据库加载数据到缓存,这个操作不应在事务上下文中进行。 |
| NESTED | Propagation.NESTED | 在现有事务中创建一个嵌套的"子事务"。可以部分回滚,但主事务回滚会连带回滚所有嵌套事务。 | - 电商下单:主事务创建订单,嵌套事务为用户增加积分。如果积分增加失败,可以只回滚积分操作,而订单创建仍然成功。 - 批量处理:处理一个批量任务,主事务记录总进度,嵌套事务处理单个子项。某个子项失败不影响其他子项,但总事务回滚会清除所有进度。 |
四、示例代码
1. REQUIRED (默认)
@Service
public class OrderService {
@Transactional // 默认就是 REQUIRED
public void createOrder() {
// 如果调用此方法时没有事务,则创建新事务
// 如果已有事务,则加入该事务
orderDao.save(order);
}
}
2. SUPPORTS
@Service
public class ProductService {
@Transactional(propagation = Propagation.SUPPORTS)
public Product getProduct(Long id) {
// 有事务就用事务,没事务就直接查询
return productDao.findById(id);
}
}
3. MANDATORY
@Service
public class PaymentService {
@Transactional(propagation = Propagation.MANDATORY)
public void processPayment() {
// 必须在事务中调用,否则抛异常
paymentDao.process(payment);
}
}
4. REQUIRES_NEW
@Service
public class AuditService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logAction(String action) {
// 总是开启新事务,挂起当前事务(如果存在)
auditDao.save(action);
}
}
5. NOT_SUPPORTED
@Service
public class NotificationService {
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void sendEmail(String to) {
// 非事务执行,挂起当前事务(如果存在)
emailClient.send(to, message);
}
}
6. NEVER
@Service
public class CacheService {
@Transactional(propagation = Propagation.NEVER)
public void warmUpCache() {
// 必须在非事务环境中执行,有事务则抛异常
cache.preloadData();
}
}
7. NESTED
@Service
public class OrderService {
@Transactional(propagation = Propagation.NESTED)
public void addPoints(User user, int points) {
// 在嵌套事务中执行,可以单独回滚
userDao.addPoints(user, points);
}
}







844

被折叠的 条评论
为什么被折叠?



