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

1.spring

Transaction rolled back because it has been marked as rollback-only
事務已回滾,由於它被標記成了只回滾
<prop key="query*">PROPAGATION_REQUIRED,readOnly</prop>
query開頭的方法readOnly,因此只能select,拋出異常,insert/update/delete操做必然回滾app

2.測試

發現selectA調用selectB,若是selectB拋出Exception,selectA中捕獲Exception可是並不繼續向外拋出,最後會出現錯誤。this


糾其原理其實很簡單,在selectB返回的時候,transaction被設置爲rollback-only了,可是selectA正常消化掉,沒有繼續向外拋。
那麼selectA結束的時候,transaction會執commit操做,可是 transaction已經被設置爲 rollback-only了。
因此會出現這個錯誤。
有的同窗說了,那不是沒得搞了,service不能拋出異常,或者不能攔截異常了?
其實否則,其實錯誤不在這裏,而是select這種操做爲何要啓動事務呢?spa

3.demo示例代碼code

1.applicationContext.xml配置事務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測試代碼blog

@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不回滾,不報以上錯誤。get

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錯誤。

相關文章
相關標籤/搜索