事务传播问题案例分析

问题描述

有些非核心逻辑我们不希望抛异常而导致核心逻辑事务回滚时,我们往往考虑对于非核心逻辑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
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容