国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

解決org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back beca

這篇具有很好參考價(jià)值的文章主要介紹了解決org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back beca。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

1. 復(fù)現(xiàn)錯(cuò)誤


今天本地使用Knife4j調(diào)用后端接口時(shí),報(bào)出如下圖的錯(cuò)誤:

解決org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back beca,免費(fèi)專欄,spring,java,spring boot,后端,spring cloud

于是,查看后端控制臺(tái)的輸出結(jié)果,如下所示:

org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:752)
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:654)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:407)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)
	at com.test.cloud.service.ModelService$$EnhancerBySpringCGLIB$$a05f18b.generatePage(<generated>)
	at com.test.cloud.controller.ModelController.generatePage(ModelController.java:169)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1063)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:652)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	......

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

之前也遇到這個(gè)錯(cuò)誤,但由于不太懂事務(wù)的嵌套,且總不能重現(xiàn)這個(gè)錯(cuò)誤。

趁著此次的錯(cuò)誤,徹底研究該錯(cuò)誤形成的原因。

2. 分析錯(cuò)誤


Transaction silently rolled back because it has been marked as rollback-only翻譯成中文,即事務(wù)以靜默方式回滾,因?yàn)樗驯粯?biāo)記為僅回滾。

那么,這句話是什么意思呢?通過對(duì)以下代碼的分析,來了解它的含義。

因?yàn)楣敬a不能泄露,特寫如下三個(gè)測(cè)試類:

  1. CommonApplicationTestsspring boot自帶的測(cè)試類
@SpringBootTest
class CommonApplicationTests {

  @Autowired
  TestRollbackService testRollbackService;

  public void testRollback(){
    try{
      testRollbackService.functionOne();
    }catch(Exception e){
      e.printStackTrace();
    }
  }
}
  1. TestRollbackService:事務(wù)回滾類
@Autowired
private UserMapper userMapper;

@Autowired
private TestTransactionService testTransactionService;

@Transactional(rollbackFor = BizException.class)
public void functionOne(){
	try{
      	User updateUser = new User();
    	updateUser.setId(new Long(19));
    	updateUser.setName("super先生");
    	userMapper.updateByPrimaryKey(updateUser);
        testTransactionService.functionTwo();
    }catch(Exception e){
        e.printStackTrace();
    }
}
  1. TestTransactionService:事務(wù)執(zhí)行類
@Autowired
private AddressMapper addressMapper;
 
@Transactional(rollbackFor = BizException.class)
public void functionTwo(){
  Address addressUpdate=new Address();
  addressUpdate.setId(1L);
  addressUpdate.setDetail("無錫市經(jīng)開區(qū)")
  addressMapper.updateByPrimaryKey(addressUpdate);
  System.out.println(1/0);
}

如上兩個(gè)方法,分別為functionOnefunctionTwo。

functionOne加上了 @Transactional(rollbackFor = BizException.class)。

與此同時(shí),functionTwo也加上了 @Transactional(rollbackFor = BizException.class)

你在functionOne中調(diào)用了functionTwo,而functionTwo報(bào)出了錯(cuò)誤信息,即可觸發(fā)回滾異常的報(bào)錯(cuò)。

兩個(gè)方法都加了事務(wù)注解,它們都會(huì)受到到事務(wù)管理的攔截器增強(qiáng),并且事務(wù)傳播的方式都是默認(rèn)的,也就是REQUIRED,當(dāng)已經(jīng)存在事務(wù)的時(shí)候就加入事務(wù),沒有就創(chuàng)建事務(wù)。

這里functionOnefunctionTwo都受事務(wù)控制,并且是處于同一個(gè)事務(wù)的。

functionOne調(diào)用了functionTwo,functionOne中抓了functionTwo的異常。當(dāng)functionTwo發(fā)生異常時(shí),functionTwo的操作應(yīng)該回滾。

functionOne吃了異常,functionOne方法中沒有產(chǎn)生異常,所以functionOne的操作又應(yīng)該提交,二者是相互矛盾的。

spring的事務(wù)關(guān)聯(lián)攔截器在抓到functionTwo的異常后,就會(huì)標(biāo)記rollback-only為true。當(dāng)functionOne執(zhí)行完準(zhǔn)備提交后,發(fā)現(xiàn)rollback-only為true,也會(huì)回滾,并拋出異常告訴調(diào)用者。

程序時(shí)序圖如下:

解決org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back beca,免費(fèi)專欄,spring,java,spring boot,后端,spring cloud

3. 分析spring的事務(wù)機(jī)制

3.1 入口程序


程序入口走cglib的代理類(CglibAopProxy)。

找到CglibAopProxyDynamicAdvisedInterceptor.class內(nèi)部類,可以看到入口方法(intercept)如下:

@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;
    Object target = null;
    TargetSource targetSource = this.advised.getTargetSource();

    Object var16;
    .....
}

在該方法(intercept)中的找到如下語句:

解決org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back beca,免費(fèi)專欄,spring,java,spring boot,后端,spring cloud

即語句retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();

點(diǎn)擊上圖中CglibMethodInvocation類,即可到CglibAopProxyCglibMethodInvocation內(nèi)部類的如下方法:

解決org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back beca,免費(fèi)專欄,spring,java,spring boot,后端,spring cloud

然后找到TransactionInterceptor類,即到了事務(wù)管理的攔截器,如下圖所示:

解決org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back beca,免費(fèi)專欄,spring,java,spring boot,后端,spring cloud

3.2 事務(wù)管理的主方法


invokeWithinTransaction是事務(wù)管理的主方法。

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, TransactionAspectSupport.InvocationCallback invocation) throws Throwable {
    TransactionAttributeSource tas = this.getTransactionAttributeSource();
    TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null;
    TransactionManager tm = this.determineTransactionManager(txAttr);
    if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
        boolean isSuspendingFunction = KotlinDetector.isSuspendingFunction(method);
        boolean hasSuspendingFlowReturnType = isSuspendingFunction && "kotlinx.coroutines.flow.Flow".equals((new MethodParameter(method, -1)).getParameterType().getName());
        if (isSuspendingFunction && !(invocation instanceof TransactionAspectSupport.CoroutinesInvocationCallback)) {
            throw new IllegalStateException("Coroutines invocation not supported: " + method);
        } else {
            TransactionAspectSupport.CoroutinesInvocationCallback corInv = isSuspendingFunction ? (TransactionAspectSupport.CoroutinesInvocationCallback)invocation : null;
            TransactionAspectSupport.ReactiveTransactionSupport txSupport = (TransactionAspectSupport.ReactiveTransactionSupport)this.transactionSupportCache.computeIfAbsent(method, (key) -> {
                Class<?> reactiveType = isSuspendingFunction ? (hasSuspendingFlowReturnType ? Flux.class : Mono.class) : method.getReturnType();
                ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(reactiveType);
                if (adapter == null) {
                    throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " + method.getReturnType());
                } else {
                    return new TransactionAspectSupport.ReactiveTransactionSupport(adapter);
                }
            });
            TransactionAspectSupport.InvocationCallback callback = invocation;
            if (corInv != null) {
                callback = () -> {
                    return CoroutinesUtils.invokeSuspendingFunction(method, corInv.getTarget(), corInv.getArguments());
                };
            }

            Object result = txSupport.invokeWithinTransaction(method, targetClass, callback, txAttr, (ReactiveTransactionManager)tm);
            if (corInv != null) {
                Publisher<?> pr = (Publisher)result;
                return hasSuspendingFlowReturnType ? TransactionAspectSupport.KotlinDelegate.asFlow(pr) : TransactionAspectSupport.KotlinDelegate.awaitSingleOrNull(pr, corInv.getContinuation());
            } else {
                return result;
            }
        }
    } else {
        PlatformTransactionManager ptm = this.asPlatformTransactionManager(tm);
        String joinpointIdentification = this.methodIdentification(method, targetClass, txAttr);
        if (txAttr != null && ptm instanceof CallbackPreferringPlatformTransactionManager) {
            TransactionAspectSupport.ThrowableHolder throwableHolder = new TransactionAspectSupport.ThrowableHolder();

            Object result;
            try {
                result = ((CallbackPreferringPlatformTransactionManager)ptm).execute(txAttr, (statusx) -> {
                    TransactionAspectSupport.TransactionInfo txInfo = this.prepareTransactionInfo(ptm, txAttr, joinpointIdentification, statusx);

                    Object var9;
                    try {
                        Object retVal = invocation.proceedWithInvocation();
                        if (retVal != null && vavrPresent && TransactionAspectSupport.VavrDelegate.isVavrTry(retVal)) {
                            retVal = TransactionAspectSupport.VavrDelegate.evaluateTryFailure(retVal, txAttr, statusx);
                        }

                        var9 = retVal;
                        return var9;
                    } catch (Throwable var13) {
                        if (txAttr.rollbackOn(var13)) {
                            if (var13 instanceof RuntimeException) {
                                throw (RuntimeException)var13;
                            }

                            throw new TransactionAspectSupport.ThrowableHolderException(var13);
                        }

                        throwableHolder.throwable = var13;
                        var9 = null;
                    } finally {
                        this.cleanupTransactionInfo(txInfo);
                    }

                    return var9;
                });
            } catch (TransactionAspectSupport.ThrowableHolderException var22) {
                throw var22.getCause();
            } catch (TransactionSystemException var23) {
                if (throwableHolder.throwable != null) {
                    this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                    var23.initApplicationException(throwableHolder.throwable);
                }

                throw var23;
            } catch (Throwable var24) {
                if (throwableHolder.throwable != null) {
                    this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                }

                throw var24;
            }

            if (throwableHolder.throwable != null) {
                throw throwableHolder.throwable;
            } else {
                return result;
            }
        } else {
            TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

            Object retVal;
            try {
                retVal = invocation.proceedWithInvocation();
            } catch (Throwable var20) {
                this.completeTransactionAfterThrowing(txInfo, var20);
                throw var20;
            } finally {
                this.cleanupTransactionInfo(txInfo);
            }

            if (retVal != null && vavrPresent && TransactionAspectSupport.VavrDelegate.isVavrTry(retVal)) {
                TransactionStatus status = txInfo.getTransactionStatus();
                if (status != null && txAttr != null) {
                    retVal = TransactionAspectSupport.VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
                }
            }

            this.commitTransactionAfterReturning(txInfo);
            return retVal;
        }
    }
}

程序執(zhí)行的是else分支,步驟很清晰

  1. 獲取TransactionAttribute

  2. 基于TransactionAttribute獲取TransactionManager

  3. 基于TransactionAttribute獲取joinpointIdentification(沒研究什么作用)

  4. 基于1,2,3創(chuàng)建的對(duì)象獲取 TransactionAspectSupport.TransactionInfotransactionInfoTransactionAspectSupport的一個(gè)內(nèi)部類

  5. 執(zhí)行業(yè)務(wù)方法

  6. 抓到異常就回滾,并清除事務(wù),然后向上拋異常;沒有異常就清除事務(wù),然后提交。

對(duì)象之間的關(guān)聯(lián)關(guān)系如下圖所示:

解決org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back beca,免費(fèi)專欄,spring,java,spring boot,后端,spring cloud

3.3 細(xì)究各對(duì)象的獲取


TransactionManager的獲取比較簡單,程序里獲取到的其實(shí)就是自己配置的bean。

創(chuàng)建TransactionInfo的過程中要先獲取TransactionStatus

TransactionStatus又需要拿到ConnectionHolder。

  1. createTransactionIfNecessary

createTransactionIfNecessary方法在類TransactionAspectSupport中。

protected TransactionAspectSupport.TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
    if (txAttr != null && ((TransactionAttribute)txAttr).getName() == null) {
        txAttr = new DelegatingTransactionAttribute((TransactionAttribute)txAttr) {
            public String getName() {
                return joinpointIdentification;
            }
        };
    }

    TransactionStatus status = null;
    if (txAttr != null) {
        if (tm != null) {
            status = tm.getTransaction((TransactionDefinition)txAttr);
        } else if (this.logger.isDebugEnabled()) {
            this.logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + "] because no transaction manager has been configured");
        }
    }

    return this.prepareTransactionInfo(tm, (TransactionAttribute)txAttr, joinpointIdentification, status);
}
  1. 獲取TransactionStatus

這里通過status = tm.getTransaction((TransactionDefinition)txAttr)語句來創(chuàng)建TransactionStatus。

接著看語句中的getTransaction方法,它在在類AbstractPlatformTransactionManager中,如下所示:

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
    Object transaction = this.doGetTransaction();
    boolean debugEnabled = this.logger.isDebugEnabled();
    if (definition == null) {
        definition = new DefaultTransactionDefinition();
    }

    if (this.isExistingTransaction(transaction)) {
        return this.handleExistingTransaction((TransactionDefinition)definition, transaction, debugEnabled);
    } else if (((TransactionDefinition)definition).getTimeout() < -1) {
        throw new InvalidTimeoutException("Invalid transaction timeout", ((TransactionDefinition)definition).getTimeout());
    } else if (((TransactionDefinition)definition).getPropagationBehavior() == 2) {
        throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");
    } else if (((TransactionDefinition)definition).getPropagationBehavior() != 0 && ((TransactionDefinition)definition).getPropagationBehavior() != 3 && ((TransactionDefinition)definition).getPropagationBehavior() != 6) {
        if (((TransactionDefinition)definition).getIsolationLevel() != -1 && this.logger.isWarnEnabled()) {
            this.logger.warn("Custom isolation level specified but no actual transaction initiated; isolation level will effectively be ignored: " + definition);
        }

        boolean newSynchronization = this.getTransactionSynchronization() == 0;
        return this.prepareTransactionStatus((TransactionDefinition)definition, (Object)null, true, newSynchronization, debugEnabled, (Object)null);
    } else {
        AbstractPlatformTransactionManager.SuspendedResourcesHolder suspendedResources = this.suspend((Object)null);
        if (debugEnabled) {
            this.logger.debug("Creating new transaction with name [" + ((TransactionDefinition)definition).getName() + "]: " + definition);
        }

        try {
            boolean newSynchronization = this.getTransactionSynchronization() != 2;
            DefaultTransactionStatus status = this.newTransactionStatus((TransactionDefinition)definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
            this.doBegin(transaction, (TransactionDefinition)definition);
            this.prepareSynchronization(status, (TransactionDefinition)definition);
            return status;
        } catch (Error | RuntimeException var7) {
            this.resume((Object)null, suspendedResources);
            throw var7;
        }
    }
}
  1. 獲取transactionStatus前先獲取DataSourceTransactionObject

通過程序最上面的語句 Object transaction = this.doGetTransaction();來創(chuàng)建DataSourceTransactionObject對(duì)象,這是DataSourceTransactionManager的內(nèi)部類

我們來看語句中的doGetTransaction方法,它在類DataSourceTransactionManager中,如下代碼所示:

protected Object doGetTransaction() {
   DataSourceTransactionManager.DataSourceTransactionObject txObject = new DataSourceTransactionManager.DataSourceTransactionObject();
   txObject.setSavepointAllowed(this.isNestedTransactionAllowed());
   ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(this.obtainDataSource());
   txObject.setConnectionHolder(conHolder, false);
   return txObject;
}

同時(shí),這里獲取了ConnectionHolder對(duì)象,此處的newConnectionHolderfalse

接著看doGetTransaction方法中的TransactionSynchronizationManager.getResource方法,如下代碼所示:

@Nullable
public static Object getResource(Object key) {
    Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
    Object value = doGetResource(actualKey);
    if (value != null && logger.isTraceEnabled()) {
        logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
    }
    return value;
}

通過一個(gè)key獲取的,類似于從一個(gè)池子里面拿東西一樣。

其實(shí)當(dāng)functionOne方法執(zhí)行時(shí),并沒有獲取到ConnectionHolder,拿到的是null

我們繼續(xù)看getResource方法中的doGetResource方法。

doGetResource方法是在類TransactionSynchronizationManager中。

private static Object doGetResource(Object actualKey) {
    Map<Object, Object> map = (Map)resources.get();
    if (map == null) {
        return null;
    } else {
        Object value = map.get(actualKey);
        if (value instanceof ResourceHolder && ((ResourceHolder)value).isVoid()) {
            map.remove(actualKey);
            if (map.isEmpty()) {
                resources.remove();
            }
 
            value = null;
        }
 
        return value;
    }
}

resources對(duì)象其實(shí)是一個(gè)ThreadLocal,意思是同一個(gè)線程中拿到的ConnectionHolder是相同的。

  1. doBegin方法

doBegin方法是在類DataSourceTransactionManager中。

protected void doBegin(Object transaction, TransactionDefinition definition) {
    DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction;
    Connection con = null;
 
    try {
        if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
            Connection newCon = this.obtainDataSource().getConnection();
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
            }
 
            txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
        }
 
        txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
        con = txObject.getConnectionHolder().getConnection();
        Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
        txObject.setPreviousIsolationLevel(previousIsolationLevel);
        if (con.getAutoCommit()) {
            txObject.setMustRestoreAutoCommit(true);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
            }
 
            con.setAutoCommit(false);
        }
 
        this.prepareTransactionalConnection(con, definition);
        txObject.getConnectionHolder().setTransactionActive(true);
        int timeout = this.determineTimeout(definition);
        if (timeout != -1) {
            txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
        }
 
        if (txObject.isNewConnectionHolder()) {
            TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());
        }
 
    } catch (Throwable var7) {
        if (txObject.isNewConnectionHolder()) {
            DataSourceUtils.releaseConnection(con, this.obtainDataSource());
            txObject.setConnectionHolder((ConnectionHolder)null, false);
        }
 
        throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", var7);
    }
}

截取該方法的重要幾行:

Connection newCon = this.obtainDataSource().getConnection();
if (this.logger.isDebugEnabled()) {
    this.logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
 
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);

先獲取連接(java.sql.Connection),接著創(chuàng)建ConnectionHolder。

newConnectionHolder設(shè)置為true,如果之前不為空,newConnectionHolder則為false

如果newConnectionHoldertrue,還需要將connectionHolder放到threadLocal里面,讓后面的方法可以獲取到相同的ConnectionHolder,截取的代碼如下:


if (txObject.isNewConnectionHolder()) {
    TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());
}

到此,TransactionStatus就創(chuàng)建好了。

  1. 獲取TransactionInfo

首先看類TransactionAspectSupport中的prepareTransactionInfo。

protected TransactionAspectSupport.TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, String joinpointIdentification, @Nullable TransactionStatus status) {
    TransactionAspectSupport.TransactionInfo txInfo = new TransactionAspectSupport.TransactionInfo(tm, txAttr, joinpointIdentification);
    if (txAttr != null) {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
        }
 
        txInfo.newTransactionStatus(status);
    } else if (this.logger.isTraceEnabled()) {
        this.logger.trace("Don't need to create transaction for [" + joinpointIdentification + "]: This method isn't transactional.");
    }
 
    txInfo.bindToThread();
    return txInfo;
}

我們細(xì)看txInfo.bindToThread();方法。

bindToThread方法在類TransactionAspectSupport.TransactionInfo中,如下代碼所示:

private void bindToThread() {
    this.oldTransactionInfo = (TransactionAspectSupport.TransactionInfo)TransactionAspectSupport.transactionInfoHolder.get();
    TransactionAspectSupport.transactionInfoHolder.set(this);
}
java.lang.ThreadLocal#get 

java.lang.ThreadLocal類中的get方法如下:

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

bindToThread()的的作用是獲取oldTransactionInfo,還有線程有有關(guān)。

4. functionTwo方法拋異常后的回滾操作


我們首先看類TransactionAspectSupport中的completeTransactionAfterThrowing方法,如下所示:

protected void completeTransactionAfterThrowing(@Nullable TransactionAspectSupport.TransactionInfo txInfo, Throwable ex) {
    if (txInfo != null && txInfo.getTransactionStatus() != null) {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex);
        }
 
        if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
            try {
                txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
            } catch (TransactionSystemException var6) {
                this.logger.error("Application exception overridden by rollback exception", ex);
                var6.initApplicationException(ex);
                throw var6;
            } catch (Error | RuntimeException var7) {
                this.logger.error("Application exception overridden by rollback exception", ex);
                throw var7;
            }
        } else {
            try {
                txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
            } catch (TransactionSystemException var4) {
                this.logger.error("Application exception overridden by commit exception", ex);
                var4.initApplicationException(ex);
                throw var4;
            } catch (Error | RuntimeException var5) {
                this.logger.error("Application exception overridden by commit exception", ex);
                throw var5;
            }
        }
    }
}

txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());調(diào)用transactionManager進(jìn)行rollback。

接著看AbstractPlatformTransactionManager類中的rollback方法,如下所示:

public final void rollback(TransactionStatus status) throws TransactionException {
    if (status.isCompleted()) {
        throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");
    } else {
        DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status;
        this.processRollback(defStatus, false);
    }
}

進(jìn)一步調(diào)自身的processRollback,那就繼續(xù)看類中AbstractPlatformTransactionManagerprocessRollback方法,如下所示:

private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
    try {
        boolean unexpectedRollback = unexpected;
 
        try {
            this.triggerBeforeCompletion(status);
            if (status.hasSavepoint()) {
                if (status.isDebug()) {
                    this.logger.debug("Rolling back transaction to savepoint");
                }
 
                status.rollbackToHeldSavepoint();
            } else if (status.isNewTransaction()) {
                if (status.isDebug()) {
                    this.logger.debug("Initiating transaction rollback");
                }
 
                this.doRollback(status);
            } else {
                if (status.hasTransaction()) {
                    if (!status.isLocalRollbackOnly() && !this.isGlobalRollbackOnParticipationFailure()) {
                        if (status.isDebug()) {
                            this.logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
                        }
                    } else {
                        if (status.isDebug()) {
                            this.logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
                        }
 
                        this.doSetRollbackOnly(status);
                    }
                } else {
                    this.logger.debug("Should roll back transaction but cannot - no transaction available");
                }
 
                if (!this.isFailEarlyOnGlobalRollbackOnly()) {
                    unexpectedRollback = false;
                }
            }
        } catch (Error | RuntimeException var8) {
            this.triggerAfterCompletion(status, 2);
            throw var8;
        }
 
        this.triggerAfterCompletion(status, 1);
        if (unexpectedRollback) {
            throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");
        }
    } finally {
        this.cleanupAfterCompletion(status);
    }
 
}

this.triggerBeforeCompletion(status) 方法好像釋放了連接。

functionTwo不是新事務(wù),所以最后會(huì)執(zhí)行this.doSetRollbackOnly(status)

進(jìn)而看類DataSourceTransactionManager中的doSetRollbackOnly方法,如下所示:

protected void doSetRollbackOnly(DefaultTransactionStatus status) {
    DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();
    if (status.isDebug()) {
        this.logger.debug("Setting JDBC transaction [" + txObject.getConnectionHolder().getConnection() + "] rollback-only");
    }
 
    txObject.setRollbackOnly();
}

接著看方法doSetRollbackOnly中的DataSourceTransactionObject.setRollbackOnly()方法,如下所示:

public void setRollbackOnly() {
    this.getConnectionHolder().setRollbackOnly();
}

這里,可以看到最終設(shè)置的是connectionHolderrollbackonly屬性。

5. functionOne方法嘗試提交時(shí)的操作


我們首先看TransactionAspectSupport類中的commitTransactionAfterReturning方法,如下所示:

protected void commitTransactionAfterReturning(@Nullable TransactionAspectSupport.TransactionInfo txInfo) {
    if (txInfo != null && txInfo.getTransactionStatus() != null) {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
        }
 
        txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
    }
 
}

同樣的,這里調(diào)用transactionManager進(jìn)行提交。

緊著看AbstractPlatformTransactionManager中的commit方法,如下所示:

public final void commit(TransactionStatus status) throws TransactionException {
    if (status.isCompleted()) {
        throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");
    } else {
        DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status;
        if (defStatus.isLocalRollbackOnly()) {
            if (defStatus.isDebug()) {
                this.logger.debug("Transactional code has requested rollback");
            }
 
            this.processRollback(defStatus, false);
        } else if (!this.shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
            if (defStatus.isDebug()) {
                this.logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
            }
 
            this.processRollback(defStatus, true);
        } else {
            this.processCommit(defStatus);
        }
    }
}

這個(gè)方法判斷了一些無法提交的情況,程序這里走第二個(gè)分支,部分代碼如下:

else if (!this.shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
    if (defStatus.isDebug()) {
        this.logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
    }
 
    this.processRollback(defStatus, true);
}

判斷條件為:

  1. 全局不是rollbackonly時(shí),也提交(這個(gè)可能是一個(gè)配置的參數(shù),配合在rollbackonly的時(shí)候也提交,也就是出現(xiàn)現(xiàn)在這種情況后,不用回滾,直接提交)

  2. 并且全局是rollbackonly

進(jìn)而看類DefaultTransactionStatus中的isGlobalRollbackOnly方法,如下所示:

public boolean isGlobalRollbackOnly() {
    return this.transaction instanceof SmartTransactionObject && ((SmartTransactionObject)this.transaction).isRollbackOnly();
}

這里又要滿足兩個(gè)條件:

  1. 這里的transactionDataSourceTransactionObject

DataSourceTransaction繼承JdbcTransactionObjectSupport

JdbcTransactionObjectSupport又實(shí)現(xiàn)SmartTransactionObject,所以第一個(gè)條件滿足。

  1. DatSourceTransactionObjectRollbackOnlygetset方法如下:
public void setRollbackOnly() {
    this.getConnectionHolder().setRollbackOnly();
}
 
public boolean isRollbackOnly() {
    return this.getConnectionHolder().isRollbackOnly();
}

之前functionTwo方法拋出異常時(shí),就是調(diào)用的DataSourceTransactionObjectset方法設(shè)置rollbackonlytrue,現(xiàn)在再用get方法獲取,只要是同一個(gè)connectionHolder

functionOne獲取到的rollbackOnly就是true,就會(huì)觸發(fā)回滾,執(zhí)行this.processRollback(defStatus, true)。

最后再次看類AbstractPlatformTransactionManager中的processRollback方法,如下所示:

private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
    try {
        boolean unexpectedRollback = unexpected;
 
        try {
            this.triggerBeforeCompletion(status);
            if (status.hasSavepoint()) {
                if (status.isDebug()) {
                    this.logger.debug("Rolling back transaction to savepoint");
                }
 
                status.rollbackToHeldSavepoint();
            } else if (status.isNewTransaction()) {
                if (status.isDebug()) {
                    this.logger.debug("Initiating transaction rollback");
                }
 
                this.doRollback(status);
            } else {
                if (status.hasTransaction()) {
                    if (!status.isLocalRollbackOnly() && !this.isGlobalRollbackOnParticipationFailure()) {
                        if (status.isDebug()) {
                            this.logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
                        }
                    } else {
                        if (status.isDebug()) {
                            this.logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
                        }
 
                        this.doSetRollbackOnly(status);
                    }
                } else {
                    this.logger.debug("Should roll back transaction but cannot - no transaction available");
                }
 
                if (!this.isFailEarlyOnGlobalRollbackOnly()) {
                    unexpectedRollback = false;
                }
            }
        } catch (Error | RuntimeException var8) {
            this.triggerAfterCompletion(status, 2);
            throw var8;
        }
 
        this.triggerAfterCompletion(status, 1);
        if (unexpectedRollback) {
            throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");
        }
    } finally {
        this.cleanupAfterCompletion(status);
    }
}

到這里unexpectedRollbacktrue,就拋出了Transaction rolled back because it has been marked as rollback-only這個(gè)異常了。

6. 總結(jié)


以上就是Transaction silently rolled back because it has been marked as rollback-only錯(cuò)誤來分析spring boot的事務(wù)機(jī)制。

如果有不同意見的,可以在評(píng)論區(qū)中留言,大家共同進(jìn)步。

如果你對(duì)Knife4j感興趣,可以參考博文:https://blog.csdn.net/lvoelife/article/details/128114264文章來源地址http://www.zghlxwxcb.cn/news/detail-730643.html

到了這里,關(guān)于解決org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back beca的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 解決問題 Could not obtain transaction-synchronized Session for current thread

    解決問題 Could not obtain transaction-synchronized Session for current thread

    一、問題現(xiàn)象 在使用Hibernate的項(xiàng)目中,在一個(gè)定時(shí)任務(wù)中,執(zhí)行某段代碼,滿足條件后,更新表數(shù)據(jù)。 程序在執(zhí)行到更新表數(shù)據(jù)的時(shí)候,報(bào)錯(cuò)如下: 二、解決方案 在 spring-config.xml 配置事務(wù) 切入點(diǎn) 和 切入方法 [說明] 1、tx:method name=“dispose*” propagation=“REQUIRED” / 表示對(duì)

    2024年02月08日
    瀏覽(42)
  • MySql報(bào)1205:1205 - Lock wait timeout exceeded; try restarting transaction,出現(xiàn)1205如何解決

    MySql報(bào)1205:1205 - Lock wait timeout exceeded; try restarting transaction,出現(xiàn)1205如何解決

    問題:當(dāng)在mysql執(zhí)行一個(gè)DDL語句時(shí)候,報(bào)1205. 本來想刪除一段時(shí)間的數(shù)據(jù),語句如下: 報(bào)錯(cuò)如下:1205 - Lock wait timeout exceeded; try restarting transaction,主要是源數(shù)據(jù)都是屁了insert的,可能沒有提交,資源被占,現(xiàn)在殺掉這個(gè) 鎖住的進(jìn)程id就OK。 1.執(zhí)行?SHOW FULL PROCESSLIST,找到這個(gè)

    2024年02月13日
    瀏覽(15)
  • 已解決org.springframework.beans.factory.UnsatisfiedDependencyException org.springframework.beans.factor

    已解決org.springframework.beans.factory.UnsatisfiedDependencyException org.springframework.beans.factory.異常的正確解決方法,親測(cè)有效!??! org.springframework.beans.factory.UnsatisfiedDependencyException org.springframework.beans.factor 對(duì)于 org.springframework.beans.factory.UnsatisfiedDependencyException 異常,通常是由于依賴注

    2024年02月05日
    瀏覽(20)
  • pgsql報(bào)錯(cuò)current transaction is aborted.commands ignored until end of transaction block

    這個(gè)錯(cuò)誤翻譯過來是: 當(dāng)前事務(wù)已中止。在事務(wù)塊結(jié)束之前,要求被忽略 意思就是在pgsql中,同一事務(wù)中如果某次數(shù)據(jù)庫操作出錯(cuò)了,那么當(dāng)前事務(wù)中這個(gè)操作以后的所有命令都將出錯(cuò)。 進(jìn)行修改的話就是 可以增加檢測(cè)機(jī)制,當(dāng)我們檢測(cè)事務(wù)中有sql失敗時(shí),可以通過回滾

    2024年02月04日
    瀏覽(21)
  • 【Redis】Transaction(事務(wù))

    【Redis】Transaction(事務(wù))

    Redis事務(wù)是一個(gè)組有多個(gè)Redis命令的集合,這些命令可以作為一個(gè)原子操作來執(zhí)行。 Redis事務(wù)通常用于以下兩種情況: 保證操作的原子性:在多個(gè)命令的執(zhí)行過程中,如果有一個(gè)命令執(zhí)行失敗,整個(gè)事務(wù)都需要回滾(撤銷)到事務(wù)開始前的狀態(tài),確保數(shù)據(jù)的一致性。 實(shí)現(xiàn)樂觀

    2024年02月13日
    瀏覽(20)
  • Hive(20):Transaction事務(wù)

    1 Hive事務(wù)背景知識(shí) Hive本身從設(shè)計(jì)之初時(shí),就是不支持事務(wù)的,因?yàn)镠ive的核心目標(biāo)是將已經(jīng)存在的結(jié)構(gòu)化數(shù)據(jù)文件映射成為表,然后提供基于表的SQL分析處理,是一款面向分析的工具。 并且Hive映射的數(shù)據(jù)通常存儲(chǔ)于HDFS上,而HDFS是不支持隨機(jī)修改文件數(shù)據(jù)的。 這個(gè)定位就意

    2024年02月16日
    瀏覽(66)
  • Transaction事務(wù)使用了解

    Transaction事務(wù)使用了解

    ? 在wiki的解釋中,事務(wù)是一組單元化的操作,這組操作可以保證要么全部成功,要么全部失?。ㄖ灰幸粋€(gè)失敗的操作,就會(huì)把其他已經(jīng)成功的操作回滾)。 ? 這樣的解釋還是不夠直觀,看下面一個(gè)經(jīng)典的例子。假設(shè)有兩個(gè)銀行賬戶A和B,現(xiàn)在A要給B轉(zhuǎn)10塊錢,也就是轉(zhuǎn)賬。

    2024年02月16日
    瀏覽(31)
  • 【Verifying transaction: failed】

    【Verifying transaction: failed】

    錯(cuò)誤場(chǎng)景:anaconda prompt中輸入命令conda update conda出現(xiàn)如下錯(cuò)誤 用戶沒有對(duì)anaconda3文件夾的讀寫權(quán)限,造成其原因可能是由于在安裝anaconda時(shí)使用了管理員權(quán)限 根據(jù)提示的環(huán)境安裝路徑 找到文件夾、點(diǎn)擊右鍵后下滑找到屬性 進(jìn)入 安全 組或用戶名 選擇users ,修改權(quán)限-完全控制

    2024年02月16日
    瀏覽(30)
  • spring-transaction源碼分析(3)Transactional事務(wù)失效原因

    spring-transaction源碼分析(3)Transactional事務(wù)失效原因

    在Transactional方法中使用this方式調(diào)用另一個(gè)Transactional方法時(shí),攔截器無法攔截到被調(diào)用方法,嚴(yán)重時(shí)會(huì)使事務(wù)失效。 類似以下代碼: 正常情況下,執(zhí)行到\\\"繼續(xù)插入數(shù)據(jù)\\\"時(shí)會(huì)拋出一個(gè)\\\"rollback only\\\"的異常,然后事務(wù)回滾。 而現(xiàn)在的現(xiàn)象是: 三個(gè)操作都不會(huì)開啟事務(wù),出現(xiàn)異常

    2024年02月03日
    瀏覽(30)
  • 【Mysql】事物處理(TransAction Processing)

    【Mysql】事物處理(TransAction Processing)

    ? 博主簡介:想進(jìn)大廠的打工人 博主主頁: @xyk: 所屬專欄:?JavaEE初階 最近在復(fù)習(xí)mysql,復(fù)習(xí)到了mysql事物處理(TransAction),幫自己回顧一下,如果你也想了解什么是mysql的事物處理,希望這篇文章會(huì)對(duì)你有幫助?。。≌拈_始: 目錄 文章目錄 一、事物的概念 二、為什么使

    2023年04月18日
    瀏覽(20)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包