文章目录 1. 复现错误2. 分析错误3. 分析spring的事务机制3.1 入口程序3.2 事务管理的主方法3.3 细究各对象的获取 4. functionTwo方法抛异常后的回滚操作5
今天本地使用Knife4j调用后端接口时,报出如下图的错误:

于是,查看后端控制台的输出结果,如下所示:
org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-onlyat 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
之前也遇到这个错误,但由于不太懂事务的嵌套,且总不能重现这个错误。
趁着此次的错误,彻底研究该错误形成的原因。
将Transaction silently rolled back because it has been marked as rollback-only翻译成中文,即事务以静默方式回滚,因为它已被标记为仅回滚。
那么,这句话是什么意思呢?通过对以下代码的分析,来了解它的含义。
因为公司代码不能泄露,特写如下三个测试类:
CommonApplicationTests:Spring Boot自带的测试类@SpringBootTestclass CommonApplicationTests { @Autowired TestRollbackService testRollbackService; public void testRollback(){ try{ testRollbackService.functionOne(); }catch(Exception e){ e.printStackTrace(); } }} TestRollbackService:事务回滚类@Autowiredprivate UserMapper userMapper;@Autowiredprivate 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(); }} TestTransactionService:事务执行类@Autowiredprivate AddreSSMapper addressMapper; @Transactional(rollbackFor = BizException.class)public void functionTwo(){ Address addressUpdate=new Address(); addressUpdate.setId(1L); addressUpdate.setDetail("无锡市经开区") addressMapper.updateByPrimaryKey(addressUpdate); System.out.println(1/0);} 如上两个方法,分别为functionOne和functionTwo。
functionOne加上了 @Transactional(rollbackFor = BizException.class)。
与此同时,functionTwo也加上了 @Transactional(rollbackFor = BizException.class)。
你在functionOne中调用了functionTwo,而functionTwo报出了错误信息,即可触发回滚异常的报错。
两个方法都加了事务注解,它们都会受到到事务管理的拦截器增强,并且事务传播的方式都是默认的,也就是REQUIRED,当已经存在事务的时候就加入事务,没有就创建事务。
这里functionOne和functionTwo都受事务控制,并且是处于同一个事务的。
functionOne调用了functionTwo,functionOne中抓了functionTwo的异常。当functionTwo发生异常时,functionTwo的操作应该回滚。
但functionOne吃了异常,functionOne方法中没有产生异常,所以functionOne的操作又应该提交,二者是相互矛盾的。
spring的事务关联拦截器在抓到functionTwo的异常后,就会标记rollback-only为true。当functionOne执行完准备提交后,发现rollback-only为true,也会回滚,并抛出异常告诉调用者。
程序时序图如下:

程序入口走cglib的代理类(CglibAopProxy)。
找到CglibAopProxy的DynamicAdvisedInterceptor.class内部类,可以看到入口方法(intercept)如下:
@Nullablepublic 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)中的找到如下语句:

即语句retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();
点击上图中CglibMethodInvocation类,即可到CglibAopProxy的CglibMethodInvocation内部类的如下方法:

然后找到TransactionInterceptor类,即到了事务管理的拦截器,如下图所示:

invokeWithinTransaction是事务管理的主方法。
@Nullableprotected 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; } }} 程序执行的是else分支,步骤很清晰
获取TransactionAttribute
基于TransactionAttribute获取TransactionManager
基于TransactionAttribute获取joinpointIdentification(没研究什么作用)
基于1,2,3创建的对象获取 TransactionAspectSupport.TransactionInfo。transactionInfo是TransactionAspectSupport的一个内部类
执行业务方法
抓到异常就回滚,并清除事务,然后向上抛异常;没有异常就清除事务,然后提交。
对象之间的关联关系如下图所示:

TransactionManager的获取比较简单,程序里获取到的其实就是自己配置的bean。
创建TransactionInfo的过程中要先获取TransactionStatus。
TransactionStatus又需要拿到ConnectionHolder。
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);} 这里通过status = tm.getTransaction((TransactionDefinition)txAttr)语句来创建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; } }} 通过程序最上面的语句 Object transaction = this.doGetTransaction();来创建DataSourceTransactionObject对象,这是DataSourceTransactionManager的内部类
我们来看语句中的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;} 同时,这里获取了ConnectionHolder对象,此处的newConnectionHolder为false
接着看doGetTransaction方法中的TransactionSynchronizationManager.getResource方法,如下代码所示:
@Nullablepublic 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;} 通过一个key获取的,类似于从一个池子里面拿东西一样。
其实当functionOne方法执行时,并没有获取到ConnectionHolder,拿到的是null。
我们继续看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对象其实是一个ThreadLocal,意思是同一个线程中拿到的ConnectionHolder是相同的。
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),接着创建ConnectionHolder。
newConnectionHolder设置为true,如果之前不为空,newConnectionHolder则为false。
如果newConnectionHolder为true,还需要将connectionHolder放到threadLocal里面,让后面的方法可以获取到相同的ConnectionHolder,截取的代码如下:
if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());} 到此,TransactionStatus就创建好了。
首先看类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;} 我们细看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,还有线程有有关。
我们首先看类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());调用transactionManager进行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); }} 进一步调自身的processRollback,那就继续看类中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); } } this.triggerBeforeCompletion(status) 方法好像释放了连接。
functionTwo不是新事务,所以最后会执行this.doSetRollbackOnly(status)。
进而看类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();} 这里,可以看到最终设置的是connectionHolder的rollbackonly属性。
我们首先看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()); } } 同样的,这里调用transactionManager进行提交。
紧着看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); } }} 这个方法判断了一些无法提交的情况,程序这里走第二个分支,部分代码如下:
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);} 判断条件为:
全局不是rollbackonly时,也提交(这个可能是一个配置的参数,配合在rollbackonly的时候也提交,也就是出现现在这种情况后,不用回滚,直接提交)
并且全局是rollbackonly
进而看类DefaultTransactionStatus中的isGlobalRollbackOnly方法,如下所示:
public boolean isGlobalRollbackOnly() { return this.transaction instanceof SmartTransactionObject && ((SmartTransactionObject)this.transaction).isRollbackOnly();} 这里又要满足两个条件:
transaction是DataSourceTransactionObjectDataSourceTransaction继承JdbcTransactionObjectSupport
JdbcTransactionObjectSupport又实现SmartTransactionObject,所以第一个条件满足。
DatSourceTransactionObject的RollbackOnly的get和set方法如下:public void setRollbackOnly() { this.getConnectionHolder().setRollbackOnly();} public boolean isRollbackOnly() { return this.getConnectionHolder().isRollbackOnly();} 之前functionTwo方法抛出异常时,就是调用的DataSourceTransactionObject的set方法设置rollbackonly为true,现在再用get方法获取,只要是同一个connectionHolder。
functionOne获取到的rollbackOnly就是true,就会触发回滚,执行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); }} 到这里unexpectedRollback为true,就抛出了Transaction rolled back because it has been marked as rollback-only这个异常了。
以上就是Transaction silently rolled back because it has been marked as rollback-only错误来分析spring boot的事务机制。
如果有不同意见的,可以在评论区中留言,大家共同进步。
如果你对Knife4j感兴趣,可以参考博文:https://blog.csdn.net/lvoelife/article/details/128114264
来源地址:https://blog.csdn.net/lvoelife/article/details/129165965
--结束END--
本文标题: 解决org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back beca
本文链接: https://www.lsjlt.com/news/428067.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-04-01
2024-04-03
2024-04-03
2024-01-21
2024-01-21
2024-01-21
2024-01-21
2023-12-23
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0