java:事务传播(含图解)

一、概念

事务传播描述一个方法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的事务;
A不在事务内,则抛异常

4. REQUIRES_NEW挂起当前,新建事务新建事务

A在事务内,挂起A所在的事务,B则新建事务;
A不在事务内,B则新建事务;

5. NOT_SUPPORTED挂起当前,非事务运行非事务运行

A在事务内,挂起A所在的事务,B则非事务运行;
A不在事务内,B则非事务运行;

6. NEVER抛异常非事务运行

A在事务内,抛异常
A不在事务内,B则非事务运行;

7. NESTED嵌套在当前事务中(Savepoint)新建事务

A在事务内,B在当前事务中建事务;
A不在事务内,B则新建事务运行;

三、使用场景

传播行为英文枚举值使用场景描述典型业务场景示例
REQUIRED (默认)Propagation.REQUIRED需要在一个事务中运行。如果已经存在事务就加入,不存在则创建新事务。- 用户注册:需要同时向用户表插入数据、初始化用户配置表,这两个操作必须在同一事务中。
- 订单创建:扣减库存、生成订单、计算积分必须在同一事务中成功或回滚。
SUPPORTSPropagation.SUPPORTS支持当前事务,但不需要强事务保证。在事务中则用事务,不在事务中就非事务执行。- 商品信息查询:在修改商品信息的业务流中调用查询,会加入事务保证读到最新数据;但前台直接查询商品详情时,不需要事务。
- 数据报表统计:对数据一致性要求不高的统计查询。
MANDATORYPropagation.MANDATORY强制必须在已有事务中运行。如果没有活动事务则抛出异常,用于防止误调用。- 资金结算:该操作绝对不能在没有事务保护的情况下单独运行,必须由上层事务方法调用。
- 核心业务步骤:某个步骤是更大业务流程的一部分,不允许独立执行。
REQUIRES_NEWPropagation.REQUIRES_NEW总是开启一个独立的新事务。暂停当前事务(如果存在),新事务与原有事务完全独立。- 操作日志记录:无论主业务(如转账)成功还是失败,操作日志都必须被记录,不能回滚。
- 审计信息写入:业务操作的审计跟踪需要独立持久化。
- 通知第三方系统:调用外部API发送通知,不希望因为外部系统响应慢或失败而回滚主业务。
NOT_SUPPORTEDPropagation.NOT_SUPPORTED明确不在事务中运行。如果存在当前事务,则挂起它,以非事务方式执行。- 发送短信/邮件:这些操作本身不具备事务性,且不希望因它们失败或耗时较长而拖累主数据库事务。
- 调用非事务性存储:向Redis、Elasticsearch等非关系型数据库写入数据。
NEVERPropagation.NEVER坚决不在事务中运行。如果检测到当前存在事务,则直接抛出异常。- 性能敏感查询:执行非常耗时的数据查询,明确要求不能有任何事务开销,如果被意外地在事务中调用,则快速失败。
- 数据缓存预热:从数据库加载数据到缓存,这个操作不应在事务上下文中进行。
NESTEDPropagation.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);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值