- 1、模型
- 2、解耦
- 3、實現
- 3.1 核心接口
- 3.2 代碼分析
- 3.2.1 事務管理
- 3.2.2 數據訪問
- 4、使用
- 4.1 編程模式
- 4.2 配置模式
- 4.2.1 聲明式配置方式
- 4.2.2 注解式配置方式
- 5、總結
1、模型
在一般的編程習慣中,Spring的數據訪問和事務處理的層次結構歸納如下圖所示:
圖. 1
2、解耦
Spring事務作為一個獨立的組件,其目的就是為了與數據訪問組件進行分離,這也是Spring事務框架設計的原則。根據這一職責清晰的原則,Spring在設計時就對事務和數據訪問進行了很好的職責划分,這個可以從spring-tx和spring-jdbc這兩個包就可以看出來。
但是在實際的代碼中,會遇到一個棘手的問題:事務和數據訪問操作都需要同一個數據庫連接資源,那么它們之間怎么傳遞呢?
這里涉及三個方面:一是線程安全,二是資源的唯一性,三是事務和數據訪問的解耦。
圖. 2
在圖2中的1、2和3這三個地方都需要使用數據庫連接,並且是同一個連接。Spring的做法是將該連接放在一個統一的地方,要使用該資源,都從這個地方獲取,這樣就解決了事務模塊和數據訪問模塊之間的緊耦合。
解除耦合之后,對於不同的ORM技術,則需要提供不同的事務管理實現,如下圖所示:
圖. 3
3、實現
3.1 核心接口
Spring事務框架的核心接口是:TransactionDefinition,TransactionStatus和PlatformTransactionManager。
TransactionDefinition用於定義事務屬性,包括事務的隔離級別,事務的傳播行為,事務的超時時間和是否為只讀事務。對於隔離級別和傳播行為這里就不深入了,可以網上找到很多資料。事務的超時時間就是一個事務需要在規定的時間里完成。只讀事務表示在一個事務里不允許進行寫操作。
TransactionStatus表示整個事務處理過程中的事務狀態,通過事務狀態可以進行事務的相應操作。
PlatformTransactionManager是對整個事務行為的抽象,定義了一個完整事務過程中的相關操作。對於不同的ORM技術,需要有不同的實現。
3.2 代碼分析
下面簡單分析一下事務和數據訪問之間數據庫連接資源是如何傳遞的,以JdbcTemplate為例,代碼如下:
3.2.1 事務管理
圖. 4
代碼1:事務管理器首先獲取事務對象(具體步驟請看代碼2),然后根據事務對象判斷是否已經存在事務,如果已經存在事務,則根據傳播特性做進一步處理,這里就不介紹了;如果不存在事務,則開啟一個新事務,開啟新事務的具體內容見代碼3。

1 //************************************** 代碼1: AbstractPlatformTransactionManager.java **************************** 2 public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { 3 Object transaction = doGetTransaction(); //獲取事務 4 ...... 5 if (isExistingTransaction(transaction)) { //如果已經存在事務,則根據傳播特性再進一步處理 6 // Existing transaction found -> check propagation behavior to find out how to behave. 7 return handleExistingTransaction(definition, transaction, debugEnabled); 8 } 9 10 // No existing transaction found -> check propagation behavior to find out how to proceed. 11 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { 12 throw new IllegalTransactionStateException( 13 "No existing transaction found for transaction marked with propagation 'mandatory'"); 14 } 15 else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || //如果不存在事務,則開啟一個新事務 16 definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || 17 definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { 18 SuspendedResourcesHolder suspendedResources = suspend(null); 19 20 try { 21 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); 22 DefaultTransactionStatus status = newTransactionStatus( 23 definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); 24 doBegin(transaction, definition); //開啟事務 25 prepareSynchronization(status, definition); 26 return status; 27 } 28 ...... 29 } 30 ...... 31 }
代碼2:獲取事務對象,事務對象中包含了一個數據庫連接,這個數據庫連接是從事務同步管理器中獲取的。事務同步管理器管理了一個ThreadLocal變量,它用於存放當前線程使用的數據連接資源。
代碼3:開啟事務,從事務對象中獲取連接,如果連接不存在則從數據庫連接池中獲取新的連接,關閉自動提交,設定事務超時時間,最后將數據庫連接存儲在事務同步管理中,即綁定在當前線程上。

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="meilvDataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
在類或者方法上使用注解@Transactional就可以進行事務管理了。
5、總結
Spring的事務管理和數據訪問模塊職責相當清晰,認識這一設計原則對我們學習他們具有根本性的作用,本文試圖理清兩者之間的關系,而這所謂的“關系”就是數據庫連接,這也是Spring事務和數據訪問都需要依賴的基礎。
從另一方面來看,Spring對這兩者關系的處理很值得我們學習和借鑒,不管是多線程編程的線程安全性,還是模塊之間的解耦。