分散式交易系列(1.1)Spring交易管理員PlatformTransactionManager

來源:互聯網
上載者:User

標籤:

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來完成的,如下:

    • 事務的開始,設定自動認可為false

    • 事務的提交,通過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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.