springboot @transactional 动态代理,无事务方法调用有事务方法,有事务方法抛错不回滚问题记录

可以先去看看springboot @transactional 动态代理实现

场景:

同一个类中,无事务A方法调用有事务方法B,基于业务接口不能直接抛错,需要返回指令对象。

无事务A循环多次调用有事务方法B,方法B抛错,不影响正常数据的持久层写入。方法B会涉及到几张表数据的更新

遇到的问题:B方法抛错,数据不回滚。如B方法中的判断boxSet表数据的逻辑没有报错,到判断boxOne逻辑出错,boxSet数据写进了数据库,没有回滚。

即:无事务方法调用有事务方法,事务失效

解决方案1:

将无事务A方法和有事务方法B不要放在同一个类中,就可以了。

原因:动态代理机制,一次操作,proxy不会重复代理一个对象两次,基于proxy的method.invoke..的方法,然后无事务方法A是没有加  @Transaction注解的,所以代理时也不会开启事务,B方法开启也是失效的

扫描二维码关注公众号,回复: 9008047 查看本文章

解决方案2:

获取被代理对象的proxy,代理对象去调用方法,代码如下:

启动类加上注解:

@EnableAspectJAutoProxy(exposeProxy = true)//动态代理方式事务

@EnableEurekaClient
@EnableFeignClients
//开启事务
@EnableTransactionManagement
@SpringBootApplication
@EnableAsync
@MapperScan(basePackages = { "com.hierway.vslm.dataaccess.mybatis.mapper" })
@EnableAspectJAutoProxy(exposeProxy = true)//动态代理方式事务
public class VslmApplication {

	public static void main(String[] args) {
		SpringApplication.run(VslmApplication.class, args);
	}

}

动态代理调用逻辑 :

//解决抛错事务不回滚
 CapaStatCacheComponent currentProxy = (CapaStatCacheComponent) AopContext.currentProxy();
setId = currentProxy.creatSetAndupdateByResAlloca(commandId, reqId,specId, specAllotSboxMap.getResAllocaList());

下面是解决了事务不回滚的代码:

A方法:

creatSetAndupdateBySpec

    public List<SpecAllotSboxVo> creatSetAndupdateBySpec(List<SpecAllotSboxMap> specAllotSboxMaps) {
        //第一次查询,初始化加载统计缓存信息
        if (isFirst) {
            selectToStart();
        }
        String methodStr="creatSetAndupdateBySpec";
        if (CollectionUtils.isEmpty(specAllotSboxMaps)){
            logger.error("入参为空,{}",methodStr);
            throw new ApiException(ResultCode.PARAMS_ERROR,"参数错误");
        }
        List<SpecAllotSboxVo> specAllotSboxVoList =new ArrayList<>();
        for (SpecAllotSboxMap specAllotSboxMap : specAllotSboxMaps) {
            SpecAllotSboxVo specAllotSboxVo = new SpecAllotSboxVo();
            String commandId = specAllotSboxMap.getCommandId();
            String reqId = specAllotSboxMap.getReqId();
            String specId = specAllotSboxMap.getSpecId();
            String setId =null ;
            try {
                //解决抛错事务不回滚
                CapaStatCacheComponent currentProxy = (CapaStatCacheComponent) AopContext.currentProxy();
                setId = currentProxy.creatSetAndupdateByResAlloca(commandId, reqId,specId, specAllotSboxMap.getResAllocaList());
//                setId = creatSetAndupdateByResAlloca(commandId, reqId,specId, specAllotSboxMap.getResAllocaList());
            }catch (Exception e){
                logger.error("指令{},创建set并分配资源失败,回滚:{},{}",commandId,e.getMessage(),methodStr);
                //throw new ApiException(ResultCode.FAILURE);
                specAllotSboxVo.setResultCode(ResultCode.FAILURE.getCode());
            }
            specAllotSboxVo.setCommandId(commandId);
            specAllotSboxVo.setSetId(setId);
            if (StringUtils.isEmpty(setId)){
                specAllotSboxVo.setResultCode(ResultCode.FAILURE.getCode());
            }else {
                specAllotSboxVo.setResultCode(ResultCode.SUCCESS.getCode());
            }
            specAllotSboxVoList.add(specAllotSboxVo);
        }
        return specAllotSboxVoList;
    }

B方法:

creatSetAndupdateByResAlloca

    //创建set并分配产能
    @Transactional
    public String creatSetAndupdateByResAlloca(String commandId,String reqId, String specId, List<ResAlloca> resAllocaList) {
        String methodStr="creatSetAndupdateByResAlloca";
        if (StringUtils.isEmpty(reqId) || StringUtils.isEmpty(specId)){
            logger.error("需求号和规格号不能为空,{}",methodStr);
            throw new ApiException(ResultCode.PARAMS_ERROR,"参数错误");
        }
        //创建set
        StreamBoxSet set =null ;
        String setId = null;
        /*SboxSet record = new SboxSet();
        record.setReqId(reqId);
        record.setSpecId(specId);
        List<SboxSet> setList = sboxSetDao.select(record);
        if (!CollectionUtils.isEmpty(setList)){
            setId=setList.get(0).getSetId();
        }else {*/
        StreamBoxSet streamBoxSet = new StreamBoxSet();
        streamBoxSet.setReqId(reqId);
        streamBoxSet.setSpecId(specId);
        try {
            set = sboxSetService.addNewStreamBoxSet(streamBoxSet);
        }catch (Exception e){
            logger.error("调用创建set接口报错,{}",methodStr);
            throw new ApiException(ResultCode.FAILURE,"创建set失败");
        }
        if (set==null || StringUtils.isEmpty(set.getSetId())){
            logger.error("创建set失败,set为空,{}",methodStr);
            throw new ApiException(ResultCode.FAILURE,"创建set失败");
        }
        setId = set.getSetId();
//        }
        for (ResAlloca resAlloca : resAllocaList) {
            resAlloca.setSetId(setId);
        }
        //只支持一个规格下的产能分配  多个规格分配无法和set映射
        Boolean isAlloct=false;
        try {
            isAlloct=checkUpdateByResAlloca(resAllocaList);
        }catch (ApiException e){
            logger.error("调用资源占用接口抛错,{}",methodStr);
            throw new ApiException(ResultCode.FAILURE,"资源占用失败");
        }
        if (isAlloct==false){
            logger.error("调用资源占用接口失败,{}",methodStr);
            throw new ApiException(ResultCode.FAILURE,"资源占用失败");
        }
        //return null;
        return setId;
    }
发布了109 篇原创文章 · 获赞 2 · 访问量 5702

猜你喜欢

转载自blog.csdn.net/Seven71111/article/details/103529161