在上篇文章 Spring 事務初始化源碼分析 中分析了 Spring 事務初始化的一個過程,當初始化完成後,Spring 是如何去獲取事務,當目標方法異常後,又是如何進行回滾的,又或是目標方法執行成功後,又是怎麼提交的呢?此外,事務的提交和回滾由底層數據庫進行控制,而在 Spring 事務使用詳解 中知道,Spring 事務行爲能夠傳播,這個傳播方式由 Spring 來進行控制,它是怎麼控制的呢?這篇文章就來分析下 Spring 事務提交回滾的源碼。java
還記得在 Spring 事務初始化源碼分析 中註冊了一個 bean,名字爲 TransactionInterceptor 嗎?,它就是用來執行事務功能的,它是一個方法攔截器,以下所示:數據庫
它實現了 MethodInterceptor 接口,而該接口只有一個 invoke 方法,用來執行目標方法編程
public Object invoke(MethodInvocation invocation) throws Throwable { Class<?> targetClass = (invocation.getThis() != null ?AopUtils.getTargetClass(invocation.getThis()) : null); // 調用父類的方法 return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed); }
父類的 invokeWithinTransaction 方法定義了一個事務方法執行的框架,而每一步再細分爲方法進行實現,代碼以下:框架
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation){ // 1. 獲取事務屬性 TransactionAttributeSource tas = getTransactionAttributeSource(); final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null); // 2. 獲取事務管理器 final PlatformTransactionManager tm = determineTransactionManager(txAttr); // 3. 獲取須要事務的方法名稱:類目.方法名 final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); // 4. 聲明式事務 if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { // 5. 獲取該方法上事務的信息 TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal = null; try { // 6. 目標方法執行,它是一個攔截器鏈 retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // 7. 事務回滾 completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { // 8. 清除事務信息 cleanupTransactionInfo(txInfo); } // 9. 事務提交 commitTransactionAfterReturning(txInfo); return retVal; } else { // 10. 編程式事務,流程和聲明式事務一致 } }
一個事務方法執行流程大概有如下幾個步驟:源碼分析
1. 獲取事務屬性
2. 獲取事務管理器
3. 獲取須要事務的方法名稱
5. 獲取該方法上事務的信息
6. 目標方法執行
7. 事務回滾
8. 清除事務信息
9. 事務提交ui
首先去獲取方法上面 Translational 註解的屬性,在 Spring 事務初始化源碼分析 中已經分析過了,即在 AnnotationTransactionAttributeSource.computeTransactionAttribute 中進行獲取。this
每一個事務都由對應的事務管理器,因此在事務開始錢須要獲取對應的事務管理器spa
protected PlatformTransactionManager determineTransactionManager(TransactionAttribute txAttr) { if (txAttr == null || this.beanFactory == null) { return getTransactionManager(); } // 事務管理器名稱 String qualifier = txAttr.getQualifier(); if (StringUtils.hasText(qualifier)) { return determineQualifiedTransactionManager(this.beanFactory, qualifier); } else if (StringUtils.hasText(this.transactionManagerBeanName)) { return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName); } else { // 默認事務管理器 PlatformTransactionManager defaultTransactionManager = getTransactionManager(); defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class); // ..... return defaultTransactionManager; } }
這裏主要去獲取名稱的名稱,爲 全限定類名+方法名的方式:method.getDeclaringClass().getName() + '.' + method.getName();.net
該部分是 Spring 事務最複雜的部分,好比說去建立一個事務,設置事務的隔離級別,超時時間,對事務傳播方式的處理,事務的掛起和恢復等;事務信息 TransactionInfo 包含了目標方法執行前的全部狀態信息,若是方法執行失敗,則會根據該信息來進行回滾。線程
對應方法爲:
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
代碼以下所示:
protected TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) { // 設置事務的名稱,爲方法全限定名joinpointIdentification if (txAttr != null && txAttr.getName() == null) { txAttr = new DelegatingTransactionAttribute(txAttr) { public String getName() { return joinpointIdentification; } }; } TransactionStatus status = null; if (txAttr != null) { if (tm != null) { // 獲取事務 status = tm.getTransaction(txAttr); } } // 建立事務信息 return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); }
在方法 getTransaction 中獲取事務,是最爲複雜的邏輯,在其中處理隔離級別,超時時間和傳播方式等。
public final TransactionStatus getTransaction(TransactionDefinition definition){ // 獲取事務 Object transaction = doGetTransaction(); // ... // 若是已經存在事務了,則處理事務的傳播方式,如掛起存在的事務,新建事務等 if (isExistingTransaction(transaction)) { return handleExistingTransaction(definition, transaction, debugEnabled); } // ..... // 若是不存在事務,且事務的傳播方式爲 mandatory, 則拋出異常 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { throw new IllegalTransactionStateException("...."); } else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { SuspendedResourcesHolder suspendedResources = suspend(null); // 若是事務的傳播方式爲 requested, requestes_new,nested,則會新建一個事務 try { boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); // 第三個參數爲true表示新建事務 DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); // 構造 transaction,包括隔離級別,timeout,若是是新鏈接,則綁定到當前線程 doBegin(transaction, definition); // 同步新事務 prepareSynchronization(status, definition); return status; } catch (RuntimeException | Error ex) { resume(null, suspendedResources); throw ex; } } else { boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null); } }
獲取事務 doGetTransaction(),在該方法中,會根據 DataSource 獲取一個鏈接,以下:
protected Object doGetTransaction() { DataSourceTransactionObject txObject = new DataSourceTransactionObject(); //若是設置了容許嵌套事務,則開啓保存點;只有嵌套事務纔有保存點 txObject.setSavepointAllowed(isNestedTransactionAllowed()); // 根據 DataSource 獲取鏈接,ConnectionHolder爲一個數據庫鏈接 ConnectionHolder conHolder = TransactionSynchronizationManager.getResource(obtainDataSource()); txObject.setConnectionHolder(conHolder, false); return txObject; }
以後,判斷當前線程是否存在事務,若是存在事務,則根據事務的傳播方式來處理已存在的事務,這裏先不看。
若是不存在事務且事務的傳播方式爲 requested, requestes_new,nested,則會新建一個事務:
DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
//definition事務屬性 //transaction事務 //newTransaction是否事務新事務 //suspendedResources須要掛起的事務 protected DefaultTransactionStatus newTransactionStatus( TransactionDefinition definition, Object transaction, boolean newTransaction, boolean newSynchronization, boolean debug, Object suspendedResources) { boolean actualNewSynchronization = newSynchronization && !TransactionSynchronizationManager.isSynchronizationActive(); return new DefaultTransactionStatus( transaction, newTransaction, actualNewSynchronization, definition.isReadOnly(), debug, suspendedResources); }
當獲取到一個新的事務後,須要設置事務的一些信息,好比隔離級別,timeout 等,這些功能不是由 Spring 來控制,而是由底層的數據庫來控制的,數據庫鏈接的設置是在 doBegin 方法中進行處理:
protected void doBegin(Object transaction, TransactionDefinition definition) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; // 數據庫鏈接 Connection con = null; //若是當前事務不存在數據庫鏈接,或者,當前鏈接的事務同步設置爲 true,則須要獲取新的數據庫鏈接 if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) { // 獲取新鏈接 Connection newCon = obtainDataSource().getConnection(); // 事務綁定新鏈接 txObject.setConnectionHolder(new ConnectionHolder(newCon), true); } txObject.getConnectionHolder().setSynchronizedWithTransaction(true); con = txObject.getConnectionHolder().getConnection(); // 獲取和設置隔離級別 Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); txObject.setPreviousIsolationLevel(previousIsolationLevel); // 由 Spring 來控制提交方式 if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); con.setAutoCommit(false); } prepareTransactionalConnection(con, definition); // 設置當前線程存在事務的標誌 txObject.getConnectionHolder().setTransactionActive(true); // 獲取和設置超時時間 int timeout = determineTimeout(definition); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); } //若是是新鏈接,則綁定到當前線程 if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); } //其餘代碼...... } // ====獲取隔離級別 public static Integer prepareConnectionForTransaction(Connection con, TransactionDefinition definition){ // 設置只讀標識 if (definition != null && definition.isReadOnly()) { con.setReadOnly(true); //.... } // 獲取隔離級別 Integer previousIsolationLevel = null; if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) { // 從數據庫鏈接獲取隔離級別 int currentIsolation = con.getTransactionIsolation(); if (currentIsolation != definition.getIsolationLevel()) { previousIsolationLevel = currentIsolation; con.setTransactionIsolation(definition.getIsolationLevel()); } } return previousIsolationLevel; }
當設置完事務的信息後,須要把事務信息記錄在當前線程中:
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) { if (status.isNewSynchronization()) { TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction()); TransactionSynchronizationManager.setCurrentTransactionIsolationLevel( definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ? definition.getIsolationLevel() : null); TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly()); TransactionSynchronizationManager.setCurrentTransactionName(definition.getName()); TransactionSynchronizationManager.initSynchronization(); } }
如今來處理已經存在事務的狀況,
if (isExistingTransaction(transaction)) { return handleExistingTransaction(definition, transaction, debugEnabled); }
判斷是否存在事務,依據是事務中有鏈接,且 TransactionActive 爲 true
protected boolean isExistingTransaction(Object transaction) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive()); }
若是已經存在事務,則會根據事務的傳播方式來進行處理,好比 requires_new, nested 等是如何處理:
private TransactionStatus handleExistingTransaction(TransactionDefinition definition, Object transaction){ // 若是傳播方式爲 never, 則拋異常 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) { throw new IllegalTransactionStateException("..."); } // 若是傳播方式爲 not_supported, 則把當前存在的事務掛起 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) { // 掛起當前事務 Object suspendedResources = suspend(transaction); boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus(definition, null, false, newSynchronization, debugEnabled, suspendedResources); } // 若是傳播方式爲 requires_new, 則掛起當前事務,新建一個新事務 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) { // 掛起當前事務 SuspendedResourcesHolder suspendedResources = suspend(transaction); // 若是尚未激活事務,則新建事務 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); // 設置數據庫的隔離級別,timeout等 doBegin(transaction, definition); prepareSynchronization(status, definition); return status; //.... } // 若是傳播方式爲 nested,則新建事務,可是不會把存在的事務掛起,它是一個子事務 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { // 若是不支持嵌套事務,拋異常 if (!isNestedTransactionAllowed()) { throw new NestedTransactionNotSupportedException(""); } // 若是支持保存點,則建立保存點 if (useSavepointForNestedTransaction()) { DefaultTransactionStatus status = prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null); // 建立保存點 status.createAndHoldSavepoint(); return status; } else { // 若是不支持保存點,則和 requires_new 是同樣的 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } } // 若是傳播方式爲 supports和required boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null); }
掛起事務,就是把當前事務的狀態記錄下來,後續在對該事務進行恢復。
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException { if (TransactionSynchronizationManager.isSynchronizationActive()) { List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization(); Object suspendedResources = null; if (transaction != null) { suspendedResources = doSuspend(transaction); } String name = TransactionSynchronizationManager.getCurrentTransactionName(); TransactionSynchronizationManager.setCurrentTransactionName(null); boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); TransactionSynchronizationManager.setCurrentTransactionReadOnly(false); Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel(); TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null); boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive(); TransactionSynchronizationManager.setActualTransactionActive(false); return new SuspendedResourcesHolder(suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive); } //..... } // 掛起事務doSuspend protected Object doSuspend(Object transaction) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; // 把事務的鏈接置空 txObject.setConnectionHolder(null); // 從當前線程中移除 return TransactionSynchronizationManager.unbindResource(obtainDataSource()); }
當通過上面一系列操做獲取到事務信息後,再根據事務信息來封裝到 TransactionInfo 中:
protected TransactionInfo prepareTransactionInfo(PlatformTransactionManager tm, TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) { // 封裝事務信息 TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification); if (txAttr != null) { // 設置事務狀態 txInfo.newTransactionStatus(status); } }
到這裏,目標方法執行以前的事務準備工做都已作好了,以後,會調用 InvocationCallback.proceedWithInvocation 來執行目標方法,若是執行失敗,則會進行事務的回滾操做:
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) { if (txInfo != null && txInfo.getTransactionStatus() != null) { // 判斷異常是否是 RunntimeException 和 Error if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { // 回滾事務 txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); // ......... } else { // 若是是其餘類型的異常,則正常提交 txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); // ....... } } } //判斷是否回滾的異常,當前能夠經過rolbackFor屬性來修改 public boolean rollbackOn(Throwable ex) { return (ex instanceof RuntimeException || ex instanceof Error); }
public final void rollback(TransactionStatus status){ // 若是事務已完成,則回滾會拋異常 if (status.isCompleted()) { throw new IllegalTransactionStateException("...."); } DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; processRollback(defStatus, false); } // 回滾事務 private void processRollback(DefaultTransactionStatus status, boolean unexpected) { try { boolean unexpectedRollback = unexpected; // 自定義觸發器的調用,不知道幹嗎用??? triggerBeforeCompletion(status); // 若是有保存點,則回滾到保存點 if (status.hasSavepoint()) { status.rollbackToHeldSavepoint(); } else if (status.isNewTransaction()) { // 若是當前事務爲獨立的事務,則回滾 doRollback(status); } else { // 若是一個事務中又有事務,如 required,該事務能夠看做一個事務鏈, //那麼當其中的一個事務須要回滾的時候,並非立馬進行回滾, //而是隻是設置回滾狀態,到最後再統一回滾 if (status.hasTransaction()) { if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) { // 只是設置回滾狀態 doSetRollbackOnly(status); } } //....... } //.......... }finally { // 清空記錄並恢復被掛起的事務 cleanupAfterCompletion(status); } }
事務的回滾操做,若是是嵌套事務,且有保存點的話,直接回滾到保存點,嵌套事務的回滾不會影響到外部事務,也就是說,外部事務不會回滾。回滾到保存點是根據底層數據庫來操做的:
public void rollbackToHeldSavepoint() throws TransactionException { Object savepoint = getSavepoint(); // 回滾到保存點 getSavepointManager().rollbackToSavepoint(savepoint); // 釋放保存點 getSavepointManager().releaseSavepoint(savepoint); setSavepoint(null); } // 回滾到保存點 public void rollbackToSavepoint(Object savepoint) throws TransactionException { ConnectionHolder conHolder = getConnectionHolderForSavepoint(); conHolder.getConnection().rollback((Savepoint) savepoint); conHolder.resetRollbackOnly(); // ...... } // 釋放保存點 public void releaseSavepoint(Object savepoint) throws TransactionException { ConnectionHolder conHolder = getConnectionHolderForSavepoint(); conHolder.getConnection().releaseSavepoint((Savepoint) savepoint); }
若是沒有保存點,則直接回滾,也是使用數據庫的API 來操做的:
protected void doRollback(DefaultTransactionStatus status) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); con.rollback(); }
還有一種狀況, 若是一個事務中又有事務,如 required, 該事務能夠看做一個事務鏈,那麼當其中的一個事務須要回滾的時候,並非立馬進行回滾,而是隻是設置回滾狀態,到最後再統一回滾。
事務回滾後須要對事務信息進行清除:
private void cleanupAfterCompletion(DefaultTransactionStatus status) { // 設置完成狀態 status.setCompleted(); if (status.isNewSynchronization()) { TransactionSynchronizationManager.clear(); } if (status.isNewTransaction()) { // 清除事務信息 doCleanupAfterCompletion(status.getTransaction()); } if (status.getSuspendedResources() != null) { // 恢復被掛起的事務 Object transaction = (status.hasTransaction() ? status.getTransaction() : null); resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources()); } }
清除事務信息:
protected void doCleanupAfterCompletion(Object transaction) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; // 從當前線程中移除數據庫鏈接 if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.unbindResource(obtainDataSource()); } //重置數據庫鏈接 Connection con = txObject.getConnectionHolder().getConnection(); if (txObject.isMustRestoreAutoCommit()) { con.setAutoCommit(true); } DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel()); // 若是是新鏈接,則釋放鏈接 if (txObject.isNewConnectionHolder()) { DataSourceUtils.releaseConnection(con, this.dataSource); } txObject.getConnectionHolder().clear(); }
恢復被掛起的事務:
protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder){ if (resourcesHolder != null) { Object suspendedResources = resourcesHolder.suspendedResources; if (suspendedResources != null) { doResume(transaction, suspendedResources); } List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations; if (suspendedSynchronizations != null) { TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive); TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel); TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly); TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name); doResumeSynchronization(suspendedSynchronizations); } } } // 恢復事務,把事務和當前線程綁定 protected void doResume(Object transaction, Object suspendedResources) { TransactionSynchronizationManager.bindResource(obtainDataSource(), suspendedResources); }
當目標方法執行成功,沒有拋出異常,則事務能夠正常提交了;可是再上面分析事務回滾的時候,還有一種狀況沒有分析,就是若是一個事務嵌套再一個事務裏面,是一個事務鏈,若是其中的某個事務須要回滾,它並不會真正的立馬進行回滾,而是設置一個回滾標識,由最外層的事務來統一進行回滾;因此再提交事務以前,還須要進行判斷。
public final void commit(TransactionStatus status) throws TransactionException { // 若是事務已完成,則不能提交 if (status.isCompleted()) { throw new IllegalTransactionStateException("..."); } // 判斷嵌套事務是否設置了回滾標識,若是嵌套事務設置了回滾標識,則整個事務鏈都不會提交 DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; if (defStatus.isLocalRollbackOnly()) { processRollback(defStatus, false); return; } if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { processRollback(defStatus, true); return; } // 提交事務 processCommit(defStatus); }
提交事務:
private void processCommit(DefaultTransactionStatus status) throws TransactionException { try { //..... // 若是由保存點則釋放保存點 if (status.hasSavepoint()) { unexpectedRollback = status.isGlobalRollbackOnly(); status.releaseHeldSavepoint(); } else if (status.isNewTransaction()) { unexpectedRollback = status.isGlobalRollbackOnly(); // 提交 doCommit(status); } } catch (RuntimeException | Error ex) { // 若是提交過程當中出現異常,則仍是會回滾 doRollbackOnCommitException(status, ex); throw ex; } // ......... } // 數據庫鏈接進行回滾 protected void doCommit(DefaultTransactionStatus status) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); con.commit(); }
到這裏,Spring 事務的獲取,提交,回滾去分析完畢了,流程仍是比較清除的
能夠關注本人公衆號查看更多文章:Java技術大雜燴