问题描述
有些非核心逻辑我们不希望抛异常而导致核心逻辑事务回滚时,我们往往考虑对于非核心逻辑try-catch.
但有时try-catch住了,还是会回滚
案例说明
先看看下面代码,逻辑很简单.应该会有一些人认为createStore方法不会抛异常,createStore之前的zcjMerchantMapper写表成功即可提交.
先说结论: zcjMerchantMapper.insertSelective(zcjMerchant)会发生事务回滚.具体可以看代码后面原因分析过程
@GetMapping("testPropagation")
public String testPropagation(){
merchantService.createMerchant();
return "success";
}
@Service
@Slf4j
public class MerchantService {
@Autowired
private ZcjMerchantMapper zcjMerchantMapper;
@Autowired
private StoreService storeService;
@Transactional(transactionManager = "transactionManager-zcjdb", rollbackFor = Exception.class)
public void createMerchant() {
Date currentTime = new Date();
String timeStamp = System.currentTimeMillis() + "";
ZcjMerchant zcjMerchant = new ZcjMerchant();
zcjMerchant.setMerchantId(timeStamp);
zcjMerchant.setMerchantName("测试事务传播商户");
zcjMerchant.setGmtCreate(currentTime);
zcjMerchant.setGmtModified(currentTime);
// 生成商户记录
zcjMerchantMapper.insertSelective(zcjMerchant);
ZcjStore zcjStore = new ZcjStore();
zcjStore.setStoreName("测试事务传播店铺");
zcjStore.setStoreLogo(timeStamp);
zcjStore.setGmtCreate(currentTime);
zcjStore.setGmtModified(currentTime);
log.info("timeStamp->{}", timeStamp);
// 生成店铺记录
createStore(zcjStore);
}
private void createStore(ZcjStore zcjStore) {
try {
storeService.createStore(zcjStore);
} catch (Exception e) {
log.error("创建店铺过程失败,exception:", e);
}
}
}
@Service
@Slf4j
public class StoreService {
@Autowired
private ZcjStoreMapper zcjStoreMapper;
@Transactional(transactionManager = "transactionManager-zcjdb", rollbackFor = Exception.class)
public void createStore(ZcjStore zcjStore) {
// 模拟异常
int i = 1 / 0;
zcjStoreMapper.insertSelective(zcjStore);
}
}
原因分析
先看上面代码执行结果
竟然抛异常了
image.png
生成商户记录成功,但事务回滚了
image.png
可以看到异常提示: Transaction rolled back because it has been marked as rollback-only
image.png
表中也无记录
image.png
原因说明:
先看一段八股文,应该见过
Spring事务传播机制汇总如下:
PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务,就加入到这个事务中。默认策略
PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类
案例事务回滚原因说明:
2个事务嵌套,同一个transactionManager,默认事务传播机制propagation为Propagation.REQUIRED.可以理解为createStore和createMerchant同一个事务
即createStore方法异常时,不管createMerchant处理这段逻辑时有没有try-catch,该事务已经被标记需要回滚了.因此该事务依然回滚,并且会抛出这个异常
解决方案
有下面几种:
- 1.createStore方法不要事务.
- 2.采用PROPAGATION_REQUIRES_NEW传播机制
@Transactional(transactionManager = "transactionManager-zcjdb", rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void createStore(ZcjStore zcjStore) {
// 模拟异常
int i = 1 / 0;
zcjStoreMapper.insertSelective(zcjStore);
}
效果
下面是采用PROPAGATION_REQUIRES_NEW传播机制结果,不会导致事务回滚
image.png
image.png
image.png