Transaction rolled back because it has been marked as rollback-only分析解决方法

原文地址为: Transaction rolled back because it has been marked as rollback-only分析解决方法

1.

Transaction rolled back because it has been marked as rollback-only
事务已回滚,因为它被标记成了只回滚
<prop key="query*">PROPAGATION_REQUIRED,readOnly</prop>
query开头的方法readOnly,所以只能select,抛出异常,insert/update/delete操作必然回滚

2.

发现selectA调用selectB,如果selectB抛出Exception,selectA中捕获Exception但是并不继续向外抛出,最后会出现错误。


纠其原理其实很简单,在selectB返回的时候,transaction被设置为rollback-only了,但是selectA正常消化掉,没有继续向外抛。
那么selectA结束的时候,transaction会执commit操作,但是 transaction已经被设置为 rollback-only了。
所以会出现这个错误。
有的同学说了,那不是没得搞了,service不能抛出异常,或者不能拦截异常了?
其实不然,其实错误不在这里,而是select这种操作为什么要启动事务呢?

3.demo示例代码

1.applicationContext.xml配置事务

<tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- <tx:method name="sendIllegalMessage" read-only="false" rollback-for="Exception" propagation="REQUIRES_NEW" /> -->
            <tx:method name="get*" read-only="true"  />
            <tx:method name="find*" read-only="true" />
            <tx:method name="load*" read-only="true" />
            <tx:method name="query*" read-only="true" />
            <tx:method name="add*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
            <tx:method name="batchAdd*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
            <tx:method name="save*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
            <tx:method name="insert*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
            <tx:method name="update*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
            <tx:method name="modify*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
            <tx:method name="delete*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
            <tx:method name="del*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
            <tx:method name="registe*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
            <tx:method name="approve*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
            <tx:method name="clear*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
            <tx:method name="set*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
            <tx:method name="reset*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
            <tx:method name="getUpdate*" read-only="false" rollback-for="Exception" propagation="REQUIRED" />
            <tx:method name="updatedQuery*" read-only="false" rollback-for="Exception" propagation="REQUIRES_NEW" />
            <!-- <tx:method name="*" read-only="true"/> -->
        </tx:attributes>
    </tx:advice>
<aop:config>
        <aop:advisor pointcut="execution(* com.xxx.service..*Service.*(..))" advice-ref="txAdvice"/>
        <aop:advisor pointcut="execution(* com.xxx.v30.service..*Service.*(..))" advice-ref="txAdvice"/>
        <aop:advisor pointcut="execution(* com.xxx.v31.service..*Service.*(..))" advice-ref="txAdvice"/>
        <aop:advisor pointcut="execution(* com.xxx.v33.service..*Service.*(..))" advice-ref="txAdvice"/>
        <aop:advisor pointcut="execution(* com.xxx.v34.service..*Service.*(..))" advice-ref="txAdvice"/>
        <aop:advisor pointcut="execution(* com.xxx.limitCoupon.service..*Service.*(..))" advice-ref="txAdvice"/>
        <aop:advisor pointcut="execution(* com.xxx.v35.service..*Service.*(..))" advice-ref="txAdvice"/>
        <aop:advisor pointcut="execution(* com.xxx.v36.service..*Service.*(..))" advice-ref="txAdvice"/>
        <aop:advisor pointcut="execution(* com.xxx.auth.*Service.*(..))" advice-ref="txAdvice"/>
        <aop:advisor pointcut="execution(* com.xxx.notify.*Service.*(..))" advice-ref="txAdvice"/>
    </aop:config>

2.junit测试代码

@Test
    public void testCancelTask2(){
        try {
            transService.updateTransCancel2();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
public void updateTransCancel2() {
        int upCount = transMapper.updateTransCancelStat(520657512071l, 1, 0, 0,-1,-1,-1,-1,-1,-1);
        try {
            cancelTransSendSms.cancelTransSendSms2();
        } catch (Exception e) {
            e.printStackTrace();
        }
        logger.info("upCount="+upCount);
        
    }

public void cancelTransSendSms2() throws Exception{
        aotoCancel2();
    }


    private void aotoCancel2() {
        txtMap=smsConverUtil.getMessage(smsParamsMap, "RenterNoAuthDeposite", "RenterNoAuthDeposite0000");
    }

public Map<String,String> getMessage(Map<String,Object> smsParamsMap,String smsContentKey,String pushKey){
            Map<String,String> map=new LinkedHashMap<String, String>();
            String smsContent="";
            String jpushContent="";
            String smsMessage="";
            String flag="";
            logger.info("in rentNo->smsContentKey is {}",smsContentKey);
            if(StringUtils.isNotBlank(smsContentKey)){
                smsContent=getContent(smsParamsMap,smsContentKey);
                smsMessage=smsMsgDescMap.get(smsContentKey);
            }
            if(StringUtils.isNotBlank(pushKey)){
                jpushContent=getPushContentTemplate(pushKey,smsParamsMap);
                flag=pushMsgFlagMap.get(pushKey);
            }
            map.put("smsContent",smsContent);
            map.put("jpushContent",jpushContent);
            map.put("smsMessage",smsMessage);
            map.put("flag",flag);
            return map;
        }

private String getPushContentTemplate(String contentKey,Map<String,Object> contentParamMap){
            try {
                String templateContent = operationService.getTemplateMsgByAppTypeAndCode(AppTypeConstant.JPUSH, contentKey);
                if(StringUtils.isEmpty(templateContent)){
                    return  null;
                }
                
                return replaceTemplateContent(templateContent,contentParamMap);
                
            } catch (Exception e) {
                logger.error("推送消息获取消息内容报错!",e);
            }
            return null;
        }
public String getTemplateMsgByAppTypeAndCode(String appType, String textCode) {
         
        try {
            return operationTextCache.getUpdateOperateTextMsgByAppTypeAndTextCode(appType, textCode);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return null;
    }
    
    public String tgetTemplateMsgByAppTypeAndCode(String appType, String textCode) {
         
        return operationTextCache.tfindOperateTextMsgByAppTypeAndTextCode(appType, textCode);
    }
public String findOperateTextMsgByAppTypeAndTextCode(String appType,
            String textCode) {
        OperationText operationText = this.findOperationTextByAppTypeAndTextCode(appType, textCode);
        if (operationText==null) {
            throw new IllegalArgumentException("appType:"+appType+","+"textCode:"+textCode+",不存在文本模板消息");
        }else {
            return operationText.getTextMsg();
        }
    }
    
    public String getUpdateOperateTextMsgByAppTypeAndTextCode(String appType,
            String textCode) {
        OperationText operationText = this.findOperationTextByAppTypeAndTextCode(appType, textCode);
        if (operationText==null) {
            throw new IllegalArgumentException("appType:"+appType+","+"textCode:"+textCode+",不存在文本模板消息");
        }else {
            return operationText.getTextMsg();
        }
    }
    
    public String tfindOperateTextMsgByAppTypeAndTextCode(String appType,
            String textCode) {
        OperationText operationText = this.findOperationTextByAppTypeAndTextCode(appType, textCode);
        if (operationText==null) {
            throw new IllegalArgumentException("appType:"+appType+","+"textCode:"+textCode+",不存在文本模板消息");
        }else {
            return operationText.getTextMsg();
        }
    }

 

 

4.汇总(A调用B)

4.1 A无事务,B无事务(将find,get改成tfind,tget方法名)  A不回滚,不报以上错误。

4.2 A无事务,B get,find只读事务,但是不抛出throw new IllegalArgumentException("appType:"+appType+","+"textCode:"+textCode+",不存在文本模板消息");   A不回滚,不报以上错误。

4.3 A update事务,B get,find只读事务且抛出异常 (间隔捕获)   A回滚,报以上错误。

4.4 A无事务,B get,find只读事务且抛出异常  (间隔捕获)          A回滚,报以上错误。

4.5 A update事务,B update事务且抛出异常 (间隔捕获)          A回滚,报以上错误。

4.6 A update事务,B update事务且抛出异常且try..catch..B   A不回滚,不报以上错误。

4.6 A无事务,B update事务且抛出异常且try..catch..B           A不回滚,不报以上错误。

 

简单而言之:

方法1有try,方法2无try,方法3 find或get throws  A回滚,报以上错误。    捕获的异常有间隔有问题。

方法1有try,方法2有try,方法3 find或get throws  A不回滚,不报以上错误。 在抛出异常的上一级方法捕获没有问题。

 

基于以上的情况说明:类1方法1无事务,类2方法2有事务get/find无捕获,类3方法3无事务 --->报rollback-only错误。

 

基于以上的情况说明:类1方法1无事务,类2方法2有事务get/find有捕获,类3方法3无事务 --->不报rollback-only错误。     上文说的间隔try

 

 

 类1方法1无事务,类2方法2有事务get/find有无捕获,类3方法3有事务 --->报rollback-only错误。 被spring标记了rollback位,这就是为什么要REQUIRES_NEW事务了。

类1方法1无事务,类2方法2有事务updatedQuery新建事务有捕获,类3方法3有事务 --->不报rollback-only错误。

 

类1方法1无事务,类2方法2有事务updatedQuery新建事务无捕获,类3方法3有事务 --->报rollback-only错误。

 

 

 

 

 


转载请注明本文地址: Transaction rolled back because it has been marked as rollback-only分析解决方法
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值