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

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM