spring事务传播行为

Spring 事务传播行为(Propagation)指的是:一个事务方法调用另一个事务方法时,事务应该如何传播和处理

Spring 一共有 7 种事务传播行为

传播行为说明
REQUIRED 如果当前有事务,则加入;没有则新建事务(默认)
REQUIRES_NEW 无论当前是否有事务,都创建新事务
SUPPORTS 有事务就加入,没有事务就非事务执行
NOT_SUPPORTED 有事务则挂起,以非事务方式执行
MANDATORY 必须在事务中运行,没有事务则抛异常
NEVER 必须在非事务中运行,有事务则抛异常
NESTED 如果有事务,则创建嵌套事务;没有则新建事务

1. REQUIRED(默认)

 
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
}
 

特点:

  • 当前有事务 → 加入当前事务
  • 当前无事务 → 创建新事务

例如:

 
@Transactional
public void order() {
pay();
}

@Transactional
public void pay() {
}
 

执行过程:

 
order()

开启事务T1

pay()
加入事务T1
 

最终:

 
order异常

pay一起回滚
 

这是最常用的传播行为。


2. REQUIRES_NEW

 
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodA() {
}
 

特点:

无论外部有没有事务:

 
先挂起当前事务
再开启新事务
 

例如:

 
@Transactional
public void order() {

saveOrder();

saveLog();
}
 
 
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog() {
}
 

执行:

 
order()

事务T1

saveLog()

挂起T1
开启T2
提交T2

恢复T1
 

图示:

 
T1
├─saveOrder
├─T2(saveLog)
└─继续T1
 

如果:

 
saveLog成功
order失败
 

结果:

 
T2提交
T1回滚
 

日志保留。


经典场景

操作日志:

 
下单

记录日志

订单失败
 

希望:

 
订单回滚
日志保留
 

就用:

 
REQUIRES_NEW
 

3. SUPPORTS

 
@Transactional(propagation = Propagation.SUPPORTS)
 

特点:

 
有事务 → 加入
无事务 → 不开启事务
 

例如:

 
public void query() {
getUser();
}
 
 
@Transactional(propagation = SUPPORTS)
public User getUser() {
}
 

此时:

 
无事务执行
 

如果:

 
@Transactional
public void business() {
getUser();
}
 

则:

 
加入业务事务
 

经典场景

查询接口:

 
SELECT
 

因为:

 
查询不一定需要事务
 

4. NOT_SUPPORTED

 
@Transactional(propagation = Propagation.NOT_SUPPORTED)
 

特点:

 
有事务 -> 挂起
无事务 -> 直接执行
 

例如:

 
@Transactional
public void order() {

saveOrder();

queryBigTable();
}
 
 
@Transactional(propagation = NOT_SUPPORTED)
public void queryBigTable() {
}
 

执行:

 
T1开启

saveOrder()

挂起T1

queryBigTable()

恢复T1
 

经典场景

耗时查询:

 
导出Excel
统计报表
大数据查询
 

避免长事务。


5. MANDATORY

 
@Transactional(propagation = MANDATORY)
 

特点:

 
必须存在事务
否则抛异常
 

例如:

 
mandatoryMethod();
 

如果外面没有事务:

 
IllegalTransactionStateException
 

经典场景

某些核心方法:

 
扣库存
扣余额
 

必须由上层控制事务。


6. NEVER

 
@Transactional(propagation = NEVER)
 

特点:

 
必须没有事务
有事务立即报错
 

例如:

 
@Transactional
public void order() {

neverMethod();
}
 
 
@Transactional(propagation = NEVER)
public void neverMethod() {
}
 

结果:

 
抛异常
 

经典场景

禁止事务访问:

 
日志采集
监控系统
缓存查询
 

7. NESTED(嵌套事务)

 
@Transactional(propagation = NESTED)
 

特点:

利用:

 
SavePoint(保存点)
 

实现局部回滚。


例子:

 
@Transactional
public void order() {

saveOrder();

saveLog();

saveUser();
}
 
 
@Transactional(propagation = NESTED)
public void saveLog() {

int a = 1 / 0;
}
 

执行:

 
T1开始

saveOrder()

创建SavePoint

saveLog异常

回滚到SavePoint

saveUser()

提交T1
 

结果:

 
saveOrder 成功
saveLog 失败
saveUser 成功
 

NESTED 与 REQUIRES_NEW区别(面试高频)

REQUIRES_NEW

 
外层事务 T1
内层事务 T2
 

完全独立:

 
T2提交后不可回滚
 

NESTED

 
只有一个事务 T1
 

内部:

 
SavePoint
 

回滚:

 
回滚到保存点
 

图示:

 
T1
├─saveOrder
├─SavePoint
│ └─saveLog
└─saveUser
 

面试必背总结

 
REQUIRED
有则加入,无则创建(默认)

REQUIRES_NEW
总是新事务

SUPPORTS
有事务加入,无事务直接执行

NOT_SUPPORTED
挂起事务,非事务执行

MANDATORY
必须有事务

NEVER
必须没有事务

NESTED
SavePoint嵌套事务
 

高频面试题:

订单 + 日志场景应该选哪个?

 
@Transactional(propagation = REQUIRES_NEW)
 

因为:

 
订单回滚
日志不回滚
 

这是面试中最经典的事务传播行为应用场景。

 
请用真实应用中的代码说明 这几种事务传播级别在生产中的应用
 

面试的时候,如果只是背概念,面试官一般会继续追问:

你在项目中实际用过哪些传播行为?

为什么用它?

不用会出现什么问题?

实际上生产环境最常用的是:

 
REQUIRED       (90%)
REQUIRES_NEW (8%)
NOT_SUPPORTED (1%)
NESTED (1%)
 

其他几个几乎见不到。


1. REQUIRED(最常用)

场景:下单

OrderService

 
@Service
public class OrderService {

@Autowired
private StockService stockService;

@Autowired
private AccountService accountService;

@Transactional
public void createOrder(Long userId, Long productId) {

// 扣库存
stockService.deduct(productId);

// 扣余额
accountService.deduct(userId);

// 创建订单
orderMapper.insert(...);
}
}
 

StockService

 
@Transactional(propagation = Propagation.REQUIRED)
public void deduct(Long productId){
stockMapper.updateStock(productId);
}
 

AccountService

 
@Transactional(propagation = Propagation.REQUIRED)
public void deduct(Long userId){
accountMapper.updateMoney(userId);
}
 

执行流程:

 
createOrder()

开启事务T1

deductStock()
加入T1

deductMoney()
加入T1

insertOrder()
加入T1
 

如果余额不足:

 
throw new RuntimeException();
 

结果:

 
库存回滚
余额回滚
订单回滚
 

保证一致性。


2. REQUIRES_NEW

场景:订单失败也要记录日志

生产中非常常见。


下单

 
@Transactional
public void createOrder() {

try {

saveOrder();

} catch (Exception e) {

logService.saveErrorLog(e);

throw e;
}
}
 

日志服务

 
@Service
public class LogService {

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveErrorLog(Exception e){

logMapper.insert(...);
}
}
 

执行:

 
订单事务 T1

saveErrorLog()

挂起T1
开启T2
提交T2
恢复T1

T1回滚
 

结果:

 
订单失败
日志保留
 

如果不用 REQUIRES_NEW

 
@Transactional
public void saveErrorLog(){}
 

日志加入T1:

 
日志写入成功
订单异常
整个事务回滚
 

最后:

 
日志没了
 

排查问题非常痛苦。


3. REQUIRES_NEW

场景:发送站内信

比如:

 
注册送100金币

发送站内信通知
 

即使通知失败:

 
金币也不能回滚
 

用户注册

 
@Transactional
public void register(User user){

userMapper.insert(user);

rewardService.sendCoin(user);

messageService.sendMessage(user);
}
 

消息服务

 
@Transactional(propagation = REQUIRES_NEW)
public void sendMessage(User user){

messageMapper.insert(...);
}
 

结果:

 
注册送金币成功
站内信失败
 

用户依然注册成功。


4. NOT_SUPPORTED

场景:导出百万数据

很多公司都踩过坑。


错误写法:

 
@Transactional
public void exportExcel(){

List<User> users =
userMapper.queryAll();

generateExcel(users);
}
 

导出耗时:

 
5分钟
 

事务一直不提交:

 
连接占用
undo log暴涨
锁资源长期持有
 

改成:

 
@Transactional(propagation = NOT_SUPPORTED)
public void exportExcel(){

List<User> users =
userMapper.queryAll();

generateExcel(users);
}
 

执行:

 
挂起事务
非事务查询
 

避免长事务。


5. NESTED

场景:批量导入用户

需求:

 
1000条数据

998条成功
2条失败

不要全部回滚
 

ImportService

 
@Transactional
public void importUsers(List<User> users){

for(User user : users){

try{
saveUser(user);
}catch(Exception e){

log.error("导入失败");
}
}
}
 

UserService

 
@Transactional(propagation = Propagation.NESTED)
public void saveUser(User user){

userMapper.insert(user);

if(user.getAge() < 0){
throw new RuntimeException();
}
}
 

执行:

 
主事务 T1

用户1 成功
用户2 成功

创建SavePoint

用户3失败

回滚到SavePoint

用户4继续
用户5继续
 

结果:

 
999成功
1失败
 

而不是全部回滚。


6. MANDATORY

生产中较少。

场景:资金扣减

金融系统经常用。


 
@Transactional
public void transfer(){

accountService.deduct();
}
 

 
@Transactional(propagation = MANDATORY)
public void deduct(){
}
 

要求:

 
必须在事务中执行
 

如果开发人员忘记:

 
accountService.deduct();
 

直接报错:

 
IllegalTransactionStateException
 

避免资金数据异常。


7. NEVER

非常少见。

场景:缓存查询

有些查询要求:

 
绝对不能开启事务
 

例如:

 
@Transactional(propagation = NEVER)
public User getCacheUser(Long id){

return redisService.get(id);
}
 

如果有人:

 
@Transactional
public void test(){

getCacheUser();
}
 

立即报错。

避免:

 
事务污染缓存查询
 

真实互联网项目中使用频率

传播行为使用频率典型场景
REQUIRED ⭐⭐⭐⭐⭐ 下单、支付、库存
REQUIRES_NEW ⭐⭐⭐⭐ 日志、消息、通知
NOT_SUPPORTED ⭐⭐⭐ 导出、报表
NESTED ⭐⭐ 批量导入
SUPPORTS 查询接口
MANDATORY 金融扣款
NEVER 特殊缓存查询

如果是你这种做**游戏后端(下注、结算、奖励发放)**的场景,我最建议你重点掌握这几个实际案例:

REQUIRED

 
下注

扣余额

写下注记录
 

失败一起回滚。


REQUIRES_NEW

 
下注失败

记录风控日志
 

日志必须保留。


REQUIRES_NEW

 
结算成功

发送站内信
 

站内信失败不能影响结算。


NOT_SUPPORTED

 
后台导出玩家投注记录
 

避免长事务拖垮数据库。

这几个基本覆盖了大多数游戏业务和互联网业务的真实生产场景,也是面试官最爱追问的部分。

 
什么是io多路

posted on 2026-07-06 16:50  日思日睿  阅读(8)  评论(0)    收藏  举报