Java事务传播-派系之争 前言你还在死记硬背Java事务传播方式吗你是否为事务传播特性差异难以理解而苦恼吗下面我将通过一个简单的事务行为分类让你彻底理解和记住Java的七种事务传播行为。首先我们看一下Java支持哪些事务传播行为传播行为核心逻辑面对当前事务是否必须有事务REQUIRED默认有事务就加入没有就新建一个。是没有就建SUPPORTS有事务就加入没有就以非事务方式运行。否随缘MANDATORY必须已有事务否则抛异常。是必须存在REQUIRES_NEW挂起当前事务永远新建一个独立事务。是强制新建NOT_SUPPORTED挂起当前事务以非事务方式运行。否拒绝事务NEVER必须没有事务否则抛异常。否强制非事务NESTED如果存在事务则创建嵌套事务保存点否则新建事务。是存在则嵌套我们通过这些行为的特性分类为下面四种支持派、反对派、独立派和分支派派系关键字包含行为核心逻辑人话版经典场景支持派同生共死REQUIREDSUPPORTSMANDATORY有事务就加入没事务时REQUIRED建新事务、SUPPORTS不要事务、MANDATORY直接报错常规业务增删改查99%的代码都用REQUIRED独立派各不相干REQUIRES_NEWNOT_SUPPORTED挂起外面的事务自己单独玩REQUIRES_NEW新开事务NOT_SUPPORTED非事务执行审计日志业务失败也要存、大文件导出释放锁反对派拒之门外NEVER检测到有事务就直接报错绝不妥协缓存清理、全文索引防止死锁分支派主干分支NESTED寄生在主事务里但自己打标记局部失败只回滚自己主干失败全完蛋批量导入1000条里坏几条只回滚坏的那几条想象下面这个后厨工作场景主厨主事务正在炖一锅巨大的佛跳墙主事务。支持派你进来帮主厨切菜切坏了异常整锅倒掉。独立派你端走一口新锅去旁边单独炒个青菜炒糊了只倒自己这盘。反对派你看到主厨在炖佛跳墙嫌油烟大直接摔围裙走人报错要求必须关火你才进厨房。分支派NESTED主厨让你在这个大锅旁边拿个小碗调点酱汁。调坏了异常只把你小碗里的倒掉回滚到保存点大锅继续炖但如果主厨心情不好把整锅佛跳墙都泼了主事务回滚你那碗酱汁就算调好了也得跟着倒进下水道。代码样例Service public class OrderService { Autowired private InventoryService inventoryService; Autowired private AuditLogService auditLogService; Autowired private PromotionService promotionService; Autowired private NotificationService notificationService; Autowired private QueryService queryService; Autowired private SecurityService securityService; Autowired private CacheService cacheService; /** * 场景 1REQUIRED默认 * 主事务入口整个下单流程的大事务。 * 如果后续子事务抛出异常且未捕获所有 REQUIRED 和 NESTED 操作都会回滚。 */ Transactional(propagation Propagation.REQUIRED, rollbackFor Exception.class) public void placeOrder(String orderId) { System.out.println( 主事务开始 ); // 1. 扣减核心库存REQUIRED - 加入当前事务 inventoryService.deductCoreStock(orderId); // 2. 校验安全规则MANDATORY - 必须在当前事务内 securityService.verifySecurity(orderId); // 3. 批量扣减优惠券NESTED - 使用保存点部分回滚 promotionService.batchDeductCoupons(orderId); // 4. 记录操作日志REQUIRES_NEW - 独立事务不受主事务回滚影响 auditLogService.logOrderCreation(orderId); // 5. 发送短信NOT_SUPPORTED - 挂起当前事务非事务执行 notificationService.sendSms(orderId); // 6. 查询额外数据SUPPORTS - 随缘事务只读查询 queryService.getProductInfo(orderId); // 7. 清除本地缓存NEVER - 若当前有事务则抛异常 cacheService.evictLocalCache(orderId); System.out.println( 主事务提交 ); } } Service public class InventoryService { /** * 场景 2REQUIRED加入主事务 * 扣减核心库存。如果这里抛异常主事务整体回滚。 */ Transactional(propagation Propagation.REQUIRED, rollbackFor Exception.class) public void deductCoreStock(String orderId) { System.out.println( [REQUIRED] 扣减核心库存加入主事务); // 模拟数据库操作 // int rows stockMapper.deduct(product_001, 1); // if (rows 0) throw new RuntimeException(库存不足); } } Service public class SecurityService { /** * 场景 3MANDATORY必须存在事务 * 强制要求调用方必须在事务内。如果脱离事务单独调用直接抛出异常。 * 适用敏感操作强制与业务事务绑定。 */ Transactional(propagation Propagation.MANDATORY) public void verifySecurity(String orderId) { System.out.println( [MANDATORY] 校验安全规则必须在事务中); // 如果当前没有事务这里会抛出 IllegalTransactionStateException } } Service public class PromotionService { /** * 场景 4NESTED嵌套事务基于保存点 Savepoint * 批量扣减多张优惠券。假设第3张券失效仅回滚到保存点即只回滚第3张 * 不影响主事务和其他券的扣减。 */ Transactional(propagation Propagation.NESTED) public void batchDeductCoupons(String orderId) { System.out.println( [NESTED] 开始批量扣减优惠券); String[] coupons {COUPON_A, COUPON_B, COUPON_C}; for (int i 0; i coupons.length; i) { // 模拟扣减第3张券时失败 if (i 2) { System.out.println( [NESTED] 优惠券 C 扣减失败仅回滚本保存点); throw new RuntimeException(优惠券无效); } System.out.println( [NESTED] 优惠券 coupons[i] 扣减成功); } // 注意即使这里抛异常主事务中前面扣减的库存不会回滚除非主事务捕获异常后主动回滚。 // 在实际开发中通常需要捕获 NESTED 的异常决定是否继续。 } } Service public class AuditLogService { /** * 场景 5REQUIRES_NEW独立事务 * 无论主订单是否回滚比如库存不足导致下单失败 * 用户尝试下单的操作日志必须持久化保存用于审计排查。 */ Transactional(propagation Propagation.REQUIRES_NEW, rollbackFor Exception.class) public void logOrderCreation(String orderId) { System.out.println( [REQUIRES_NEW] 独立事务记录操作日志); // 这里先挂起主事务开启新连接/事务执行 insert立即提交。 } } Service public class NotificationService { /** * 场景 6NOT_SUPPORTED不支持事务 * 发送短信或邮件涉及网络IO耗时长。为了避免长时间占用数据库连接 * 挂起当前事务以非事务方式执行。 * 如果发送失败不影响主事务的回滚因为它不在事务内。 */ Transactional(propagation Propagation.NOT_SUPPORTED) public void sendSms(String orderId) { System.out.println( [NOT_SUPPORTED] 非事务发送下单短信通知); // 模拟耗时短信发送 try { Thread.sleep(100); } catch (InterruptedException e) {} // 这里异常不会导致主事务回滚因为主事务被挂起后这里无事务 } } Service public class QueryService { /** * 场景 7SUPPORTS支持事务但不强制 * 查询操作。如果当前有事务则加入并可以使用 readOnly 优化 * 如果没有事务则以普通非事务查询执行。 * 通常用于 Service 层内部的只读查询。 */ Transactional(propagation Propagation.SUPPORTS, readOnly true) public void getProductInfo(String orderId) { System.out.println( [SUPPORTS] 只读查询有事务则加入无则忽略); // select * from product where id ... } } Service public class CacheService { /** * 场景 8NEVER禁止事务 * 强制要求当前不能存在事务。如果在主事务中被调用会直接抛出异常。 * 场景某些缓存更新如 Redis或全文索引同步如果在事务中执行 * 可能会因为数据库长事务导致死锁或性能下降。 */ Transactional(propagation Propagation.NEVER) public void evictLocalCache(String orderId) { System.out.println( [NEVER] 清除本地缓存强制无事务); // 如果当前存在事务抛出 IllegalTransactionStateException } }