zoukankan      html  css  js  c++  java
  • 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错误。

     

  • 相关阅读:
    Java 代理模式
    ReentrantLock 详解
    Java线程池详解
    ConcurrentHashMap 解读
    CountDownLatch/CyclicBarrie用法记录
    微信接入笔记记录
    iOS设计模式
    iOS设计模式
    iOS设计模式
    iOS设计模式
  • 原文地址:https://www.cnblogs.com/simpledev/p/6406845.html
Copyright © 2011-2022 走看看