Transaction rolled back because it has been marked as rollback-only

目录

1.问题说明

2.示例代码

3.原因

4.解决方案


1.问题说明

Caused by: java.lang.RuntimeException: org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

 有事务的方法A调用有事务的方法B时,报错如上

2.示例代码

方法A

@Override
    @Transactional(rollbackFor = Exception.class)
    public RtnBean delBookData(InventoryDelSpecDomain delDomain, AuthUser authUser) {
        try {
            Integer docId = delDomain.getDocId();
            String id = delDomain.getId();
            DocEntryEntity docEntry = docEntryMapper.selectByPrimaryKey(docId);
            if (DocTypeEnum.EXPERIMENT_REP_LIST.getCode().equals(delDomain.getChildDocType())) {
                return wksRepsCustomizeService.deleteRecord(delDomain, authUser);
            }
            // 权限校验
            String errMsg = bookAttributeLogic.editCheck(docId, authUser, docEntry);
            if (StrUtil.isNotEmpty(errMsg)) {
                return RtnBean.build(errMsg);
            }
            // 业务校验
            RtnBean res = docTypeCheck(delDomain, docEntry);
            if (!res.getRsl()) {
                return res;
            }
            // 删除处理
            DocTypeEntity docType = docTypeMapper.selectByPrimaryKey(docEntry.getDocType());
            inventoryMapper.del(docType, id, authUser);
            // 重置
            DocColVO autoCol = docColMapper.getAutoIncCol(docEntry.getDocType());
            if (ObjectUtil.isNotEmpty(autoCol) && ObjectUtil.isNotEmpty(autoCol.getColId())) {
                nursSetBookMapper.updateSeqNo(docType.getDocType(), docId, authUser);
            }
            return RtnBean.build(ResMsg.SUCCESS);
        } catch (Exception ex) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            logTool.saveExceptionLog(authUser.getUserId(), this.getClass().getName(), "delBookData", ex);
            return RtnBean.build(ResMsg.ERROR);
        }
    }

方法B

@Override
        @Transactional(rollbackFor = Exception.class)
        public RtnBean deleteRecord(InventoryDelSpecDomain entity, AuthUser authUser) {
            try {
                String id = entity.getId();
                // id
                Integer docId = entity.getDocId();

                List delWksReps = wksRepsMapper.selectByIdList(id);
                List wksReps = wksRepsMapper.getBySetIdAndFieldId(delWksReps, docId);
                for (WksRepsEntity item : delWksReps) {
                    if (item.getUserRep() <= CommonContants.NUM_0 || item.getFieldId() <= CommonContants.NUM_0) {
                        continue;
                    }
                    WksRepsEntity rep = wksReps.stream()
                            .filter(x -> x.getSetId().equals(item.getSetId()) && x.getFieldId().equals(item.getFieldId()))
                            .filter(x -> x.getUserRep().equals(item.getUserRep()))
                            .findFirst().orElse(null);
                    if (ObjectUtil.isNotEmpty(rep) && !CommonContants.REP_STATUS_P.equals(rep.getRepStatus())) {
                        // 删除
                        wksRepsMapper.deleteByPrimaryKey(item.getBlockId(), authUser);
                        // 删除
                        expFbkMapper.deleteByBlockId(item.getBlockId());
                        // 更新
                        wksRepsMapper.updateRepNum(docId, item.getFieldId(), rep.getRepNum());
                    } else {
                        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                        return RtnBean.build(ResMsg.ERROR_ATTR_33);
                    }
                }
                return RtnBean.build(ResMsg.SUCCESS);

            } catch (Exception e) {
                // 回滚事务
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                logTool.saveExceptionLog(authUser.getUserId(), this.getClass().getName(), "deleteRecord", e);
                return RtnBean.build(ResMsg.ERROR);
            }

        }

3.原因

①在方法A中开启事务时,会执行aop增强后的方法,在方法开始前开始事务,在方法结束后提交事务,出现异常时回滚事务。

②在方法A中调用方法B,由于B的传播机制没有特殊设置,默认为PROPAGATION_REQUIRED,有事务则加入事务,没有事务则创建一个新事物。所以方法A和方法B共用一个事务。

③在方法A中调用方法B,方法B中进行了事务的回滚,然后方法B正常结束,返回方法A继续执行后面的处理,方法A执行完成之后,没有出现异常,因为执行的是aop增强后的方法,所以最后要提交事务。由于方法A和方法B共用一个事务,在方法B中进行了事务的回滚,所以,方法A执行完成要进行事务的提交时,因为已经回滚无法进行提交,所以出现此问题。

4.解决方案

方案①:

在方法B中设置事务的传播机制为PROPAGATION_REQUIRES_NEW,即使有事务也会重新开启一个新的事务。这样方法A和方法B分别在各自的事务中运行,互不干扰。

这种方式存在的问题是,方法A和方法B不再一个事务中,无法做到一致性

方法②:

不在方法B中进行回滚处理,针对正常和异常的情况放回不同的返回值,在方法A中进行判断,是继续执行还是进行回滚处理

你可能感兴趣的:(BUG,数据库)