分佈式事務(一)原理概覽html
分佈式事務(二)JTA規範java
分佈式事務(四)簡單樣例spring
分佈式事務(五)源碼詳解sql
分佈式事務(六)總結提升數據庫
本節咱們將會從上一節的」簡單樣例「入手:Spring Boot+Atomikos(TM)+Mybatis(ORM)+Mysql(DB),深刻源碼,看看這個分佈式事務是怎麼定義、執行的。緩存
先來回憶一下第二節講的JTA規範,以下圖。Atomikos是什麼角色?起到什麼做用?springboot
Atomikos根本上是一個事務管理器(TM)也就是JTA模型的核心,上圖扇形的中心位置。服務器
TM調用 【Resource Manager資源管理器】 的XAResource接口來實現事務操做。session
TM依賴 【Application Server應用服務器】 的TransactionManager接口固然若是服務器不支持事務管理,天然也就只能使用第三方包,例如Atomikos。
TM依賴 【Application應用程序】 設置事務邊界、屬性,application調用UserTransaction接口控制事務開始、提交、回滾。
org.springframework.transaction.jta.JtaTransactionManager類是spring提供的分佈式事務管理器。
JtaTransactionManager類圖以下:
實現了接口以下:
JtaTransactionManager實現了InitializingBean接口的afterPropertiesSet()方法,處於bean生命週期的容器初始化->實例化期->初始化中期,以下圖:
下面咱們看一下JtaTransactionManager在bean初始化中期InitializingBean接口的afterPropertiesSet()作了什麼:
1 /** 2 * Initialize the UserTransaction as well as the TransactionManager handle. 3 * @see #initUserTransactionAndTransactionManager() 4 */ 5 @Override 6 public void afterPropertiesSet() throws TransactionSystemException { 7 initUserTransactionAndTransactionManager(); 8 checkUserTransactionAndTransactionManager(); 9 initTransactionSynchronizationRegistry(); 10 }
1.initUserTransactionAndTransactionManager:初始化UserTransaction和TransactionManager接口。主要是若是沒有定義的話,能夠支持JNDI。
2.checkUserTransactionAndTransactionManager:校驗2個接口是否存在。UserTransaction必須定義,TransactionManager能夠不定義。
源碼以下:
對應控制檯打印:
o.s.t.jta.JtaTransactionManager : Using JTA UserTransaction: com.atomikos.icatch.jta.UserTransactionImp@614aeccc
o.s.t.jta.JtaTransactionManager : Using JTA TransactionManager: com.atomikos.icatch.jta.UserTransactionManager@5116ac09
上一節分佈式事務(三)簡單樣例中咱們配置了JtaTransactionManagerConfig類,以下:
1 package study.config.datasource; 2 3 import com.atomikos.icatch.jta.UserTransactionImp; 4 import com.atomikos.icatch.jta.UserTransactionManager; 5 import org.springframework.context.annotation.Bean; 6 import org.springframework.context.annotation.Configuration; 7 import org.springframework.transaction.jta.JtaTransactionManager; 8 9 import javax.transaction.UserTransaction; 10 11 /** 12 * 事務管理器配置類 13 * 14 * @author denny 15 */ 16 @Configuration 17 public class JtaTransactionManagerConfig { 18 19 @Bean(name = "atomikosTransactionManager") 20 public JtaTransactionManager regTransactionManager() { 21 UserTransactionManager userTransactionManager = new UserTransactionManager(); 22 UserTransaction userTransaction = new UserTransactionImp(); 23 return new JtaTransactionManager(userTransaction, userTransactionManager); 24 } 25 }
如上圖,咱們定義了一個name = "atomikosTransactionManager"的bean,具體類型爲JtaTransactionManager。其中構造了2個實現類UserTransactionImp(javax.transaction.UserTransaction接口)、UserTransactionManager(javax.transaction.TransactionManager接口)。並用這2個實現類構造了一個JtaTransactionManager。
提供給用戶操控事務的:開啓,提交,回滾等等。源碼以下:
源碼以下:
相比UserTransaction,TransactionManager接口多了接口的掛起、恢復、獲取事務3個接口。這3個方法明顯是留給系統本身調用的。
Spring 爲Atomikos定製了一個org.springframework.boot.jta.atomikos.AtomikosDataSourceBean,提供了bean生命週期的一些接口:
咱們只須要定義這個bean便可輕鬆使得spring來維護。
com.atomikos.jdbc.AtomikosDataSourceBean類圖以下:
其中核心接口:
DataSource接口:getConnection獲取數據庫鏈接
ConnectionPoolProperties接口:用於載入鏈接池的屬性
老套路哈,spring boot就這麼點花花腸子,既然使用@Transactional這種註解的方式,那麼咱們就從springboot 容器啓動時的自動配置載入(spring boot容器啓動詳解)開始看。在/META-INF/spring.factories中配置文件中查找,以下圖:
載入2個關於事務的自動配置類:
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,
因爲本文是分佈式事務,故2個配置文件都生效了,咱們先看JtaAutoConfiguration
1 /** 2 * {@link EnableAutoConfiguration Auto-configuration} for JTA. 3 * 4 * @author Josh Long 5 * @author Phillip Webb 6 * @since 1.2.0 7 */ 8 @ConditionalOnClass(javax.transaction.Transaction.class) 9 @ConditionalOnProperty(prefix = "spring.jta", value = "enabled", matchIfMissing = true) 10 @AutoConfigureBefore({ XADataSourceAutoConfiguration.class, 11 ActiveMQAutoConfiguration.class, ArtemisAutoConfiguration.class, 12 HibernateJpaAutoConfiguration.class }) 13 @Import({ JndiJtaConfiguration.class, BitronixJtaConfiguration.class, 14 AtomikosJtaConfiguration.class, NarayanaJtaConfiguration.class }) 15 @EnableConfigurationProperties(JtaProperties.class) 16 public class JtaAutoConfiguration { 17 18 }
如上,JtaAutoConfiguration這個類居然是個空殼,只有一堆註解,挑幾個重要的講一講:
1.@ConditionalOnClass(javax.transaction.Transaction.class):表明類路徑下存在javax.transaction.Transaction.class這個類,那麼JtaAutoConfiguration生效。
2.@ConditionalOnProperty(prefix = "spring.jta", value = "enabled", matchIfMissing = true),自動開啓spring.jta.enabled=true.
3.@Import({ JndiJtaConfiguration.class, BitronixJtaConfiguration.class, AtomikosJtaConfiguration.class, NarayanaJtaConfiguration.class }),又是spring套路哈,用來導入類。這裏導入了4個配置類,可見支持4種第三方事務管理器。AtomikosJtaConfiguration.class天然就是Atomikos了。
AtomikosJtaConfiguration.class這個配置類
1 @Configuration 2 @EnableConfigurationProperties({ AtomikosProperties.class, JtaProperties.class }) 3 @ConditionalOnClass({ JtaTransactionManager.class, UserTransactionManager.class }) 4 @ConditionalOnMissingBean(PlatformTransactionManager.class) 5 class AtomikosJtaConfiguration { 6 7 private final JtaProperties jtaProperties; 8 9 private final TransactionManagerCustomizers transactionManagerCustomizers; 10 11 AtomikosJtaConfiguration(JtaProperties jtaProperties, 12 ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) { 13 this.jtaProperties = jtaProperties; 14 this.transactionManagerCustomizers = transactionManagerCustomizers 15 .getIfAvailable(); 16 } 17 18 @Bean(initMethod = "init", destroyMethod = "shutdownForce") 19 @ConditionalOnMissingBean(UserTransactionService.class) 20 public UserTransactionServiceImp userTransactionService( 21 AtomikosProperties atomikosProperties) { 22 Properties properties = new Properties(); 23 if (StringUtils.hasText(this.jtaProperties.getTransactionManagerId())) { 24 properties.setProperty("com.atomikos.icatch.tm_unique_name", 25 this.jtaProperties.getTransactionManagerId()); 26 } 27 properties.setProperty("com.atomikos.icatch.log_base_dir", getLogBaseDir()); 28 properties.putAll(atomikosProperties.asProperties()); 29 return new UserTransactionServiceImp(properties); 30 } 31 32 private String getLogBaseDir() { 33 if (StringUtils.hasLength(this.jtaProperties.getLogDir())) { 34 return this.jtaProperties.getLogDir(); 35 } 36 File home = new ApplicationHome().getDir(); 37 return new File(home, "transaction-logs").getAbsolutePath(); 38 } 39 40 @Bean(initMethod = "init", destroyMethod = "close") 41 @ConditionalOnMissingBean 42 public UserTransactionManager atomikosTransactionManager( 43 UserTransactionService userTransactionService) throws Exception { 44 UserTransactionManager manager = new UserTransactionManager(); 45 manager.setStartupTransactionService(false); 46 manager.setForceShutdown(true); 47 return manager; 48 } 49 50 @Bean 51 @ConditionalOnMissingBean(XADataSourceWrapper.class) 52 public AtomikosXADataSourceWrapper xaDataSourceWrapper() { 53 return new AtomikosXADataSourceWrapper(); 54 } 55 56 @Bean 57 @ConditionalOnMissingBean 58 public static AtomikosDependsOnBeanFactoryPostProcessor atomikosDependsOnBeanFactoryPostProcessor() { 59 return new AtomikosDependsOnBeanFactoryPostProcessor(); 60 } 61 62 @Bean 63 public JtaTransactionManager transactionManager(UserTransaction userTransaction, 64 TransactionManager transactionManager) { 65 JtaTransactionManager jtaTransactionManager = new JtaTransactionManager( 66 userTransaction, transactionManager); 67 if (this.transactionManagerCustomizers != null) { 68 this.transactionManagerCustomizers.customize(jtaTransactionManager); 69 } 70 return jtaTransactionManager; 71 } 72 73 @Configuration 74 @ConditionalOnClass(Message.class) 75 static class AtomikosJtaJmsConfiguration { 76 77 @Bean 78 @ConditionalOnMissingBean(XAConnectionFactoryWrapper.class) 79 public AtomikosXAConnectionFactoryWrapper xaConnectionFactoryWrapper() { 80 return new AtomikosXAConnectionFactoryWrapper(); 81 } 82 83 } 84 85 }
這裏和本地事務分析過程一致,就再也不重複,飛機票spring事務詳解(三)源碼詳解,一直看到第二節結束.這裏只截個圖:
最終源碼調用具體事務管理器的PlatformTransactionManager接口的3個方法:
1 public interface PlatformTransactionManager { 2 // 獲取事務狀態 3 TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; 4 // 事務提交 5 void commit(TransactionStatus status) throws TransactionException; 6 // 事務回滾 7 void rollback(TransactionStatus status) throws TransactionException; 8 }
核心實現類圖:
如上提所示,PlatformTransactionManager頂級接口定義了最核心的事務管理方法,下面一層是AbstractPlatformTransactionManager抽象類,實現了PlatformTransactionManager接口的方法並定義了一些抽象方法,供子類拓展。最下面一層是2個經典事務管理器:
1.DataSourceTransactionmanager: 即本地單資源事務管理器。
2.JtaTransactionManager: 即多資源事務管理器(又叫作分佈式事務管理器),其實現了JTA規範,使用XA協議進行兩階段提交。
咱們這裏天然是JTA分佈式環境,咱們只須要從JtaTransactionManager這個實現類入手便可。
AbstractPlatformTransactionManager實現了getTransaction()方法以下:
1 @Override 2 public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { 3 Object transaction = doGetTransaction(); 4 5 // Cache debug flag to avoid repeated checks. 6 boolean debugEnabled = logger.isDebugEnabled(); 7 8 if (definition == null) { 9 // Use defaults if no transaction definition given. 10 definition = new DefaultTransactionDefinition(); 11 } 12 // 若是當前已經存在事務 13 if (isExistingTransaction(transaction)) { 14 // 根據不一樣傳播機制不一樣處理 15 return handleExistingTransaction(definition, transaction, debugEnabled); 16 } 17 18 // 超時不能小於默認值 19 if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { 20 throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout()); 21 } 22 23 // 當前不存在事務,傳播機制=MANDATORY(支持當前事務,沒事務報錯),報錯 24 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { 25 throw new IllegalTransactionStateException( 26 "No existing transaction found for transaction marked with propagation 'mandatory'"); 27 }// 當前不存在事務,傳播機制=REQUIRED/REQUIRED_NEW/NESTED,這三種狀況,須要新開啓事務,且加上事務同步 28 else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || 29 definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || 30 definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { 31 SuspendedResourcesHolder suspendedResources = suspend(null); 32 if (debugEnabled) { 33 logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition); 34 } 35 try {// 是否須要新開啓同步// 開啓// 開啓 36 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); 37 DefaultTransactionStatus status = newTransactionStatus( 38 definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); 39 doBegin(transaction, definition);// 開啓新事務 40 prepareSynchronization(status, definition);//預備同步 41 return status; 42 } 43 catch (RuntimeException ex) { 44 resume(null, suspendedResources); 45 throw ex; 46 } 47 catch (Error err) { 48 resume(null, suspendedResources); 49 throw err; 50 } 51 } 52 else { 53 // 當前不存在事務當前不存在事務,且傳播機制=PROPAGATION_SUPPORTS/PROPAGATION_NOT_SUPPORTED/PROPAGATION_NEVER,這三種狀況,建立「空」事務:沒有實際事務,但多是同步。警告:定義了隔離級別,但並無真實的事務初始化,隔離級別被忽略有隔離級別可是並無定義實際的事務初始化,有隔離級別可是並無定義實際的事務初始化, 54 if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) { 55 logger.warn("Custom isolation level specified but no actual transaction initiated; " + 56 "isolation level will effectively be ignored: " + definition); 57 } 58 boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); 59 return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null); 60 } 61 }
上圖核心步驟就是:
其實也就是把UserTransaction封裝成一個JtaTransactionObject返回。
1 @Override 2 protected Object doGetTransaction() { 3 UserTransaction ut = getUserTransaction(); 4 if (ut == null) { 5 throw new CannotCreateTransactionException("No JTA UserTransaction available - " + 6 "programmatic PlatformTransactionManager.getTransaction usage not supported"); 7 } 8 if (!this.cacheUserTransaction) { 9 ut = lookupUserTransaction( 10 this.userTransactionName != null ? this.userTransactionName : DEFAULT_USER_TRANSACTION_NAME); 11 } 12 return doGetJtaTransaction(ut); 13 } 14 15 /** 16 * Get a JTA transaction object for the given current UserTransaction. 17 * <p>Subclasses can override this to provide a JtaTransactionObject 18 * subclass, for example holding some additional JTA handle needed. 19 * @param ut the UserTransaction handle to use for the current transaction 20 * @return the JtaTransactionObject holding the UserTransaction 21 */ 22 protected JtaTransactionObject doGetJtaTransaction(UserTransaction ut) { 23 return new JtaTransactionObject(ut); 24 }
1 @Override 2 protected void doBegin(Object transaction, TransactionDefinition definition) { 3 JtaTransactionObject txObject = (JtaTransactionObject) transaction; 4 try { 5 doJtaBegin(txObject, definition); 6 } 7 catch (NotSupportedException ex) { 8 // assume nested transaction not supported 9 throw new NestedTransactionNotSupportedException( 10 "JTA implementation does not support nested transactions", ex); 11 } 12 catch (UnsupportedOperationException ex) { 13 // assume nested transaction not supported 14 throw new NestedTransactionNotSupportedException( 15 "JTA implementation does not support nested transactions", ex); 16 } 17 catch (SystemException ex) { 18 throw new CannotCreateTransactionException("JTA failure on begin", ex); 19 } 20 }
調用JtaTransactionManager.doJtaBegin:
1 protected void doJtaBegin(JtaTransactionObject txObject, TransactionDefinition definition) 2 throws NotSupportedException, SystemException { 3 4 applyIsolationLevel(txObject, definition.getIsolationLevel()); 5 int timeout = determineTimeout(definition); 6 applyTimeout(txObject, timeout); 7 txObject.getUserTransaction().begin(); 8 }
UserTransactionImp.begin->TransactionManagerImp.begin
1 public void begin ( int timeout ) throws NotSupportedException, 2 SystemException 3 { 4 CompositeTransaction ct = null; 5 ResumePreviousTransactionSubTxAwareParticipant resumeParticipant = null; 6 7 ct = compositeTransactionManager.getCompositeTransaction(); 8 if ( ct != null && ct.getProperty ( JTA_PROPERTY_NAME ) == null ) { 9 LOGGER.logWarning ( "JTA: temporarily suspending incompatible transaction: " + ct.getTid() + 10 " (will be resumed after JTA transaction ends)" ); 11 ct = compositeTransactionManager.suspend(); 12 resumeParticipant = new ResumePreviousTransactionSubTxAwareParticipant ( ct ); 13 } 14 15 try { 16 ct = compositeTransactionManager.createCompositeTransaction ( ( ( long ) timeout ) * 1000 ); 17 if ( resumeParticipant != null ) ct.addSubTxAwareParticipant ( resumeParticipant ); 18 if ( ct.isRoot () && getDefaultSerial () ) 19 ct.getTransactionControl ().setSerial (); 20 ct.setProperty ( JTA_PROPERTY_NAME , "true" ); 21 } catch ( SysException se ) { 22 String msg = "Error in begin()"; 23 LOGGER.logWarning( msg , se ); 24 throw new ExtendedSystemException ( msg , se 25 .getErrors () ); 26 } 27 recreateCompositeTransactionAsJtaTransaction(ct); 28 }
createCompositeTransaction建立混合事務
1 public CompositeTransaction createCompositeTransaction ( long timeout ) throws SysException 2 { 3 Stack errors = new Stack(); 4 CompositeTransaction ct = null , ret = null; 5 // 獲取當前線程綁定的事務 6 ct = getCurrentTx ();
// 當前線程不存在事務 7 if ( ct == null ) {
// 建立組合事務 8 ret = service_.createCompositeTransaction ( timeout ); 9 if(LOGGER.isInfoEnabled()){ 10 LOGGER.logInfo("createCompositeTransaction ( " + timeout + " ): " 11 + "created new ROOT transaction with id " + ret.getTid ()); 12 }
// 當前線程存在事務 13 } else { 14 if(LOGGER.isInfoEnabled()) LOGGER.logInfo("createCompositeTransaction ( " + timeout + " )");
// 建立子事務 15 ret = ct.getTransactionControl ().createSubTransaction (); 16 17 } 18 Thread thread = Thread.currentThread ();
// 綁定當前線程和事務的2個映射map 19 setThreadMappings ( ret, thread ); 20 21 return ret; 22 }
若是當前線程不存在事務,建立組合事務。若是當前線程存在事務,建立子事務。
調用TransactionServiceImp的createCompositeTransaction建立混合事務
1 public CompositeTransaction createCompositeTransaction ( long timeout ) throws SysException 2 { 3 if ( !initialized_ ) throw new IllegalStateException ( "Not initialized" ); 4 5 if ( maxNumberOfActiveTransactions_ >= 0 && 6 tidToTransactionMap_.size () >= maxNumberOfActiveTransactions_ ) { 7 throw new IllegalStateException ( "Max number of active transactions reached:" + maxNumberOfActiveTransactions_ ); 8 } 9 10 String tid = tidmgr_.get (); 11 Stack lineage = new Stack (); 12 //建立協調者 15 CoordinatorImp cc = createCC ( null, tid, true, false, timeout );
// 建立組合事務 16 CompositeTransaction ct = createCT ( tid, cc, lineage, false ); 17 return ct; 18 }
事務提交流程圖以下:
AbstractPlatformTransactionManager的commit源碼以下:
1 @Override 2 public final void commit(TransactionStatus status) throws TransactionException { 3 if (status.isCompleted()) {// 若是事務已完結,報錯沒法再次提交 4 throw new IllegalTransactionStateException( 5 "Transaction is already completed - do not call commit or rollback more than once per transaction"); 6 } 7 8 DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; 9 if (defStatus.isLocalRollbackOnly()) {// 若是事務明確標記爲回滾, 10 if (defStatus.isDebug()) { 11 logger.debug("Transactional code has requested rollback"); 12 } 13 processRollback(defStatus);//執行回滾 14 return; 15 }//若是不須要全局回滾時提交 且 全局回滾 16 if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { 17 if (defStatus.isDebug()) { 18 logger.debug("Global transaction is marked as rollback-only but transactional code requested commit"); 19 }//執行回滾 20 processRollback(defStatus); 21 // 僅在最外層事務邊界(新事務)或顯式地請求時拋出「未指望的回滾異常」 23 if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) { 24 throw new UnexpectedRollbackException( 25 "Transaction rolled back because it has been marked as rollback-only"); 26 } 27 return; 28 } 29 // 執行提交事務 30 processCommit(defStatus); 31 }
如上圖,各類判斷:
processCommit以下:
1 private void processCommit(DefaultTransactionStatus status) throws TransactionException { 2 try { 3 boolean beforeCompletionInvoked = false; 4 try {//3個前置操做 5 prepareForCommit(status); 6 triggerBeforeCommit(status); 7 triggerBeforeCompletion(status); 8 beforeCompletionInvoked = true;//3個前置操做已調用 9 boolean globalRollbackOnly = false;//新事務 或 全局回滾失敗 10 if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) { 11 globalRollbackOnly = status.isGlobalRollbackOnly(); 12 }//1.有保存點,即嵌套事務 13 if (status.hasSavepoint()) { 14 if (status.isDebug()) { 15 logger.debug("Releasing transaction savepoint"); 16 }//釋放保存點 17 status.releaseHeldSavepoint(); 18 }//2.新事務 19 else if (status.isNewTransaction()) { 20 if (status.isDebug()) { 21 logger.debug("Initiating transaction commit"); 22 }//調用事務處理器提交事務 23 doCommit(status); 24 } 25 // 3.非新事務,且全局回滾失敗,可是提交時沒有獲得異常,拋出異常 27 if (globalRollbackOnly) { 28 throw new UnexpectedRollbackException( 29 "Transaction silently rolled back because it has been marked as rollback-only"); 30 } 31 } 32 catch (UnexpectedRollbackException ex) { 33 // 觸發完成後事務同步,狀態爲回滾 34 triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); 35 throw ex; 36 }// 事務異常 37 catch (TransactionException ex) { 38 // 提交失敗回滾 39 if (isRollbackOnCommitFailure()) { 40 doRollbackOnCommitException(status, ex); 41 }// 觸發完成後回調,事務同步狀態爲未知 42 else { 43 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); 44 } 45 throw ex; 46 }// 運行時異常 47 catch (RuntimeException ex) {
// 若是3個前置步驟未完成,調用前置的最後一步操做 48 if (!beforeCompletionInvoked) { 49 triggerBeforeCompletion(status); 50 }// 提交異常回滾 51 doRollbackOnCommitException(status, ex); 52 throw ex; 53 }// 其它異常 54 catch (Error err) {
// 若是3個前置步驟未完成,調用前置的最後一步操做 55 if (!beforeCompletionInvoked) { 56 triggerBeforeCompletion(status); 57 }// 提交異常回滾 58 doRollbackOnCommitException(status, err); 59 throw err; 60 } 61 62 // Trigger afterCommit callbacks, with an exception thrown there 63 // propagated to callers but the transaction still considered as committed. 64 try { 65 triggerAfterCommit(status); 66 } 67 finally { 68 triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED); 69 } 70 71 } 72 finally { 73