標籤:
1 系列目錄
- 分散式交易系列(開篇)提出疑問和研究過程
- 分散式交易系列(1.1)Spring交易管理員PlatformTransactionManager源碼分析
2 jdbc事務 2.1 例子
public void save(User user) throws SQLException{ Connection conn=jdbcDao.getConnection(); conn.setAutoCommit(false); try { PreparedStatement ps=conn.prepareStatement("insert into user(name,age) value(?,?)"); ps.setString(1,user.getName()); ps.setInt(2,user.getAge()); ps.execute(); conn.commit(); } catch (Exception e) { e.printStackTrace(); conn.rollback(); }finally{ conn.close(); }}
2.2 分析
怎麼使用事務
將自動認可設定為false,即conn.setAutoCommit(false),然後手動來conn.commit()、或者conn.rollback()。
一個重要意識
conn.commit();conn.rollback()等這些屬於事務代碼,其他執行sql的代碼屬於業務代碼
只有事務代碼和業務代碼使用的是同一個Connection的時候,事務的復原和提交才能正常執行,所以如果我們要實現事務代碼和業務代碼的分離,必須要保證他們使用的是同一個Connection。
誰在執行事務
我們可以看到,可以通過操作Connection串連來執行事務,這並不代表Connection具有事務功能,而是使用了資料庫自身的事務功能,Connection僅僅是把一些命令如commit、rollback傳遞給資料庫
2.3 存在的問題
- 1 業務代碼都要嵌套在try catch事務模板代碼中
2 當存在多個類似save(User user)的商務邏輯時,沒法保證他們的原子性
login(user);save(user);
這兩個商務邏輯都是相似的代碼,擷取Connection串連,然後執行sql語句。沒法保證它們的原子性,是因為它們使用的不是同一個串連,不在同一個事務內。
3 Hibernate的事務 3.1 例子
public void save(User user){ Session session=hibernateDao.openSession(); Transaction tx=null; try { tx=session.beginTransaction(); session.save(user); tx.commit(); } catch (Exception e) { if(tx!=null){ tx.rollback(); } }finally{ session.close(); }}
3.2 分析
事務功能和業務功能的分離
jdbc事務中Connection負擔了兩方面的功能,事務功能和執行sql的功能。這裡的Transaction是Hibernate自己定義的事務,Hibernate則把這兩者的功能單獨開來,將事務功能交給了Transaction,使得職責更加分工明確。
事務的原理
其實Session、Transaction內部會有一個相同的Connection,這樣就保證了 業務代碼和事務代碼使用的是同一個Connection,Transaction事務的復原都是依託內部的Connection來完成的,如下:
- 事務的提交,通過connection的commit方法來提交
事務的復原等操作,不再列舉
4 Spring事務功能的總體介面設計
由於上述各家實現事務功能的方式各不相同,Spring進行了統一的抽象,形成了PlatformTransactionManager交易管理員介面,事務的提交、復原等操作全部交給它來實現。Spring的事務體系也是在PlatformTransactionManager交易管理員介面上開展開來的,所以先來瞭解下PlatformTransactionManager交易管理員。
4.1 事務功能的總體介面設計
先來看下三大介面
PlatformTransactionManager : 交易管理員
TransactionDefinition : 事務的一些基礎資訊,如逾時時間、隔離等級、傳播屬性等
TransactionStatus : 事務的一些狀態資訊,如是否是一個新的事務、是否已被標記為復原
看下PlatformTransactionManager如何來操作事務:
public interface PlatformTransactionManager { //根據事務定義TransactionDefinition,擷取事務 TransactionStatus getTransaction(TransactionDefinition definition); //提交事務 void commit(TransactionStatus status); //復原事務 void rollback(TransactionStatus status);}
4.2 介面對應的實現 4.2.1 事務定義介面TransactionDefinition
- 紅線上方是一些常量定義(事務的隔離等級和事務的傳播屬性,具體不再說,網上一大堆)
- 事務的定義包含:事務的隔離等級、事務的傳播屬性、逾時時間設定、是否唯讀
要明白的地方:
事務的隔離等級是資料庫本身的事務功能,然而事務的傳播屬性則是Spring自己為我們提供的功能,資料庫事務沒有事務的傳播屬性這一說法。
該介面的實現DefaultTransactionDefinition:預設的事務定義
public class DefaultTransactionDefinition implements TransactionDefinition, Serializable { private int propagationBehavior = PROPAGATION_REQUIRED; private int isolationLevel = ISOLATION_DEFAULT; private int timeout = TIMEOUT_DEFAULT; private boolean readOnly = false; //略}
- 事務的傳播屬性為PROPAGATION_REQUIRED,即當前沒有事務的時候,建立一個,如果有則使用當前事務
- 事務的隔離等級採用底層資料庫預設的隔離等級
- 逾時時間採用底層資料庫預設的逾時時間
- 是否唯讀為false
4.2.2 事務介面定義TransactionStatus
先引出Connection串連中的儲存點功能:
//建立一個儲存點conn.setSavepoint(name);//復原到某個儲存點conn.rollback(savepoint);//釋放某個儲存點conn.releaseSavepoint(savepoint);
TransactionStatus它繼承了SavepointManager介面,SavepointManager是對事務中上述儲存點功能的封裝,如下:
public interface SavepointManager { Object createSavepoint() throws TransactionException; void rollbackToSavepoint(Object savepoint) throws TransactionException; void releaseSavepoint(Object savepoint) throws TransactionException;}
Spring利用儲存點功能實現了事務的嵌套功能。後面會詳細說明。
TransactionStatus本身更多儲存的是事務的一些狀態資訊:
- 是否是一個新的事物
- 是否有儲存點
- 是否已被標記為復原
常用的TransactionStatus介面實現為DefaultTransactionStatus:
目前jdbc事務是通過Connection來實現事務的,Hibernate是通過它自己定義的Transaction來實現的,所以各家的事務都不同,所以Spring只能以Object transaction的形式來表示各家的事務,事務的復原和提交等操作都會最終委託給上述Object transaction來完成。
Object transaction的職責就是提交復原事務,這個transaction的選擇可能如下:
- DataSourceTransactionObject
- HibernateTransactionObject
- JpaTransactionObject(之後再詳細說)
詳細資料分別如下:
對於DataSourceTransactionObject:
我們使用了dataSource來擷取串連,要想實現事務功能,必然需要使用Connection,所以它中肯定有一個Connection來執行事務的操作。
DataSourceTransactionObject中有一個ConnectionHolder,它封裝了一個Connection。
對於HibernateTransactionObject:
我們使用了hibenrate,此時要想實現事務功能,必然需要通過hibernate自己定義的Transaction來實現。
HibernateTransactionObject中含有一個SessionHolder,和上面的ConnectionHolder一樣,它封裝了一個Session,有了Session,我們就可以通過Session來產生一個Hibernate的Transaction,從而實現事務操作。
4.2.3 交易管理員介面定義PlatformTransactionManager
類圖關係如下:
重點來說下
- AbstractPlatformTransactionManager
- DataSourceTransactionManager
- HibernateTransactionManager
- JpaTransactionManager(之後詳細再說)
這就需要來看看交易管理員的介面,上述的他們都是怎麼實現的:
1 第一個介面:TransactionStatus getTransaction(TransactionDefinition definition) 根據事務定義擷取事務狀態
大體內容就是先擷取上述說明的Object transaction,判斷當前事務是否已存在,如果存在則進行事務的傳播屬性處理,後面詳細說明,如果不存在new DefaultTransactionStatus,新建立一個事務,同時使用Object transaction開啟事務。
分成了幾個過程:
- 1.1 擷取Object transaction:
不同的交易管理員擷取不同的Object transaction- DataSourceTransactionManager就是擷取上述的DataSourceTransactionObject 從當前線程中擷取綁定的ConnectionHolder,可能為null,如果為null,則會在下一個開啟事務的過程中,從dataSource中擷取一個Connection,封裝成ConnectionHolder,然後再綁定到當前線程 然後我們new 一個DataSourceTransactionObject了,具體過程如下: ![DataSourceTransactionManager擷取一個事務][7]- HibernateTransactionManager擷取HibernateTransactionObject 從當前線程中擷取綁定的SessionHolder,可能為null,如果為null,則會在下一個開啟事務的過程中從sessionFactory中擷取一個session,然後封裝成SessionHolder,然後再綁定到當前線程 然後我們就可以new 一個HibernateTransactionObject了,具體過程如下: ![HibernateTransactionManager擷取一個事務][8]
- 1.2 構建DefaultTransactionStatus,使用Object transaction開啟事務 - DataSourceTransactionManager的DataSourceTransactionObject開啟過程如下: 首先判斷之前的擷取當前線程綁定的ConnectionHolder是否為null,如果為null,從dataSource中擷取一個Connection,封裝成ConnectionHolder,然後再綁定到當前線程 因為開啟了一個事務,則必須要關閉DataSourceTransactionObject中Connection的自動認可,代碼如下(省略一些): protected void doBegin(Object transaction, TransactionDefinition definition) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; Connection con = null; //如果ConnectionHolder是否為null,從新擷取 if (txObject.getConnectionHolder() == null || txObject.getConnectionHolder().isSynchronizedWithTransaction()) { Connection newCon = this.dataSource.getConnection(); txObject.setConnectionHolder(new ConnectionHolder(newCon), true); } con = txObject.getConnectionHolder().getConnection(); Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); txObject.setPreviousIsolationLevel(previousIsolationLevel); //取消自動認可 if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); if (logger.isDebugEnabled()) { logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); } con.setAutoCommit(false); } txObject.getConnectionHolder().setTransactionActive(true); //如果是新增的ConnectionHolder,則綁定到當前線程 if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder()); } } - HibernateTransactionManager的HibernateTransactionObject開啟過程如下: 也是同樣的邏輯,如果SessionHolder為null,則從SessionFactory中擷取一個Session,然後封裝成SessionHolder,然後把這個SessionHolder綁定到當前線程 Session newSession = (entityInterceptor != null ? getSessionFactory().withOptions().interceptor(entityInterceptor).openSession() : getSessionFactory().openSession()); txObject.setSession(newSession); 同時,使用上述session開啟一個事務,把事務對象也儲存到上述的SessionHolder中。 Transaction hibTx=session.beginTransaction(); txObject.getSessionHolder().setTransaction(hibTx); 如果是新建立的SessionHolder,則綁定到當前線程 // Bind the session holder to the thread. if (txObject.isNewSessionHolder()) { TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder()); }
2 第二個介面:void rollback(TransactionStatus status) 復原事務
復原,則還是利用DefaultTransactionStatus內部的Object transaction來執行復原操作
- DataSourceTransactionManager就是使用DataSourceTransactionObject中的Connection來進行復原操作
protected void doRollback(DefaultTransactionStatus status) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); try { con.rollback(); } catch (SQLException ex) { throw new TransactionSystemException("Could not roll back JDBC transaction", ex); } }
- HibernateTransactionManager就是使用HibernateTransactionObject中的SessionHolder中的Session建立的事務Transaction來進行復原操作
protected void doRollback(DefaultTransactionStatus status) { HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction(); try { txObject.getSessionHolder().getTransaction().rollback(); } }
3 第三個介面: void commit(TransactionStatus status) 提交事務
同理,DataSourceTransactionManager依託內部的Connection來完成提交操作
HibernateTransactionManager依託內部的Transaction來完成提交操作
4.3 事務的傳播屬性解析
可以參考這篇文章的案例說明Spring事務的傳播行為和隔離等級,下面重點源碼分析下Spring的事務傳播屬性:
對於事務的傳播屬性的代碼如下:
在擷取Object transaction之後,先進行判斷,是否是已存在的事務。因為這個Object transaction的擷取過程就是直接從線程綁定的擷取的,可能當前線程已經存在事務,具體判斷如下:
DataSourceTransactionManager
protected boolean isExistingTransaction(Object transaction) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; return (txObject.getConnectionHolder() != null && txObject.getConnectionHolder().isTransactionActive());}
就是依據和當前線程綁定的ConnectionHolder中是否已存在事務
HibernateTransactionManager
public boolean hasSpringManagedTransaction() { return (this.sessionHolder != null && this.sessionHolder.getTransaction() != null);}
也是依據和當前線程綁定的SessionHolder是否已存在事務
如果是已存在事務:則需要對事務的傳播屬性進行處理,如下即上述中的的handleExistingTransaction方法:
1 PROPAGATION_NEVER:不允許存在事務,如果存在拋出異常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) { throw new IllegalTransactionStateException( "Existing transaction found for transaction marked with propagation ‘never‘");}
2 PROPAGATION_NOT_SUPPORTED:不支援事務,如果存在事務,則需將事務掛起,儲存起來,當執行完成之後,需要將掛起的事務繼續恢複
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) { if (debugEnabled) { logger.debug("Suspending current transaction"); } Object suspendedResources = suspend(transaction); boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus( definition, null, false, newSynchronization, debugEnabled, suspendedResources);}
掛起之後,產生一個Object transaction=null的事務,即不執行事務代碼,同時把掛起的資源資訊傳遞給新建立的事務,當這個事務執行完成之後,再把掛起的資源恢複過來
- 2.1 對於DataSourceTransactionManager來說,事務的掛起,就是把當前線程關聯的ConnectionHolder解除綁定:
protected Object doSuspend(Object transaction) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; txObject.setConnectionHolder(null); ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.unbindResource(this.dataSource); return conHolder; }同理事務的恢複就是把上述ConnectionHolder再重新綁定到當前線程,繼續執行該事務
- 2.2 對於HibernateTransactionManager來說,事務的掛起,就是把當前線程關聯的SessionHolder解除綁定
protected Object doSuspend(Object transaction) { HibernateTransactionObject txObject = (HibernateTransactionObject) transaction; txObject.setSessionHolder(null); SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(getSessionFactory()); txObject.setConnectionHolder(null); ConnectionHolder connectionHolder = null; if (getDataSource() != null) { connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.unbindResource(getDataSource()); } return new SuspendedResourcesHolder(sessionHolder, connectionHolder); }同理事務的恢複就是把上述SessionHolder再重新綁定到當前線程,繼續執行該事務
3 PROPAGATION_REQUIRES_NEW:開啟一個新的事務,如果當前存在事務則把當前事務掛起來
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) { SuspendedResourcesHolder suspendedResources = suspend(transaction); try { boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; }}
可以看到,建立新的事務,就會調用doBegin(transaction, definition);方法,將事務開啟。
4 PROPAGATION_NESTED : 原理很多人已詳細說明,可以參考簡單理解Spring中的PROPAGATION_NESTED,源碼證實有待繼續研究
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { if (useSavepointForNestedTransaction()) { DefaultTransactionStatus status = prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null); status.createAndHoldSavepoint(); return status; }}
這裡使用了Object transaction來建立了SavePoint,仍舊使用原事務
5 PROPAGATION_SUPPORTS 和 PROPAGATION_REQUIRED : 如果當前存在事務,則仍舊使用該事物
至此交易管理員介面就簡略說完了,還有很多細節的東西,需要各位再去仔細研究。
5 結束語
瞭解了PlatformTransactionManager交易管理員,下面就要開展Spring的編程式事務、聲明式事務,所以下一篇文章內容如下:
- TransactionTemplate可以實現編程式事務
- Spring使用AOP來實現聲明式事務
分散式交易系列(1.1)Spring交易管理員PlatformTransactionManager