1. 復(fù)現(xiàn)錯(cuò)誤
今天本地使用Knife4j
調(diào)用后端接口時(shí),報(bào)出如下圖的錯(cuò)誤:
于是,查看后端控制臺(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è)試類:
-
CommonApplicationTests
:spring boot
自帶的測(cè)試類
@SpringBootTest
class CommonApplicationTests {
@Autowired
TestRollbackService testRollbackService;
public void testRollback(){
try{
testRollbackService.functionOne();
}catch(Exception e){
e.printStackTrace();
}
}
}
-
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();
}
}
-
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è)方法,分別為functionOne
和functionTwo
。
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ù)。
這里functionOne
和functionTwo
都受事務(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í)序圖如下:
3. 分析spring的事務(wù)機(jī)制
3.1 入口程序
程序入口走cglib
的代理類(CglibAopProxy
)。
找到CglibAopProxy
的DynamicAdvisedInterceptor.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
)中的找到如下語句:
即語句retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();
點(diǎn)擊上圖中CglibMethodInvocation
類,即可到CglibAopProxy
的CglibMethodInvocation
內(nèi)部類的如下方法:
然后找到TransactionInterceptor
類,即到了事務(wù)管理的攔截器,如下圖所示:
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
分支,步驟很清晰
-
獲取
TransactionAttribute
-
基于
TransactionAttribute
獲取TransactionManager
-
基于
TransactionAttribute
獲取joinpointIdentification
(沒研究什么作用) -
基于
1,2,3
創(chuàng)建的對(duì)象獲取TransactionAspectSupport.TransactionInfo
。transactionInfo
是TransactionAspectSupport
的一個(gè)內(nèi)部類 -
執(zhí)行業(yè)務(wù)方法
-
抓到異常就回滾,并清除事務(wù),然后向上拋異常;沒有異常就清除事務(wù),然后提交。
對(duì)象之間的關(guān)聯(lián)關(guān)系如下圖所示:
3.3 細(xì)究各對(duì)象的獲取
TransactionManager
的獲取比較簡單,程序里獲取到的其實(shí)就是自己配置的bean
。
創(chuàng)建TransactionInfo
的過程中要先獲取TransactionStatus
。
TransactionStatus
又需要拿到ConnectionHolder
。
-
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);
}
-
獲取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;
}
}
}
-
獲取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ì)象,此處的newConnectionHolder
為false
接著看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
是相同的。
-
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
。
如果newConnectionHolder
為true
,還需要將connectionHolder
放到threadLocal
里面,讓后面的方法可以獲取到相同的ConnectionHolder
,截取的代碼如下:
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());
}
到此,TransactionStatus
就創(chuàng)建好了。
-
獲取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ù)看類中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
不是新事務(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è)置的是connectionHolder
的rollbackonly
屬性。
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);
}
判斷條件為:
-
全局不是
rollbackonly
時(shí),也提交(這個(gè)可能是一個(gè)配置的參數(shù),配合在rollbackonly
的時(shí)候也提交,也就是出現(xiàn)現(xiàn)在這種情況后,不用回滾,直接提交) -
并且全局是
rollbackonly
進(jìn)而看類DefaultTransactionStatus
中的isGlobalRollbackOnly
方法,如下所示:
public boolean isGlobalRollbackOnly() {
return this.transaction instanceof SmartTransactionObject && ((SmartTransactionObject)this.transaction).isRollbackOnly();
}
這里又要滿足兩個(gè)條件:
- 這里的
transaction
是DataSourceTransactionObject
DataSourceTransaction
繼承JdbcTransactionObjectSupport
JdbcTransactionObjectSupport
又實(shí)現(xiàn)SmartTransactionObject
,所以第一個(gè)條件滿足。
-
DatSourceTransactionObject
的RollbackOnly
的get
和set
方法如下:
public void setRollbackOnly() {
this.getConnectionHolder().setRollbackOnly();
}
public boolean isRollbackOnly() {
return this.getConnectionHolder().isRollbackOnly();
}
之前functionTwo
方法拋出異常時(shí),就是調(diào)用的DataSourceTransactionObject
的set
方法設(shè)置rollbackonly
為true
,現(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);
}
}
到這里unexpectedRollback
為true
,就拋出了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)步。文章來源:http://www.zghlxwxcb.cn/news/detail-730643.html
如果你對(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)!