概述spring
當咱們調用一個基於Spring的Service接口方法(如UserService#addUser())時,它將運行於Spring管理的事務 環境中,Service接口方法可能會在內部調用其它的Service接口方法以共同完成一個完整的業務操做,所以就會產生服務接口方法嵌套調用的情 況,Spring經過事務傳播行爲控制當前的事務如何傳播到被嵌套調用的目標服務接口方法中。apache
事務傳播是Spring進行事務管理的重要概念,其重要性怎麼強調都不爲過。可是事務傳播行爲也是被誤解最多的地方,在本文裏,咱們將詳細分析不一樣事務傳播行爲的表現形式,掌握它們之間的區別。ui
事務傳播行爲種類spa
Spring在TransactionDefinition接口中規定了7種類型的事務傳播行爲,它們規定了事務方法和事務方法發生嵌套調用時事務如何進行傳播:日誌
表1事務傳播行爲類型blog
事務傳播行爲類型接口 |
說明事務 |
PROPAGATION_REQUIREDip |
若是當前沒有事務,就新建一個事務,若是已經存在一個事務中,加入到這個事務中。這是最多見的選擇。ci |
PROPAGATION_SUPPORTS |
支持當前事務,若是當前沒有事務,就以非事務方式執行。 |
PROPAGATION_MANDATORY |
使用當前的事務,若是當前沒有事務,就拋出異常。 |
PROPAGATION_REQUIRES_NEW |
新建事務,若是當前存在事務,把當前事務掛起。 |
PROPAGATION_NOT_SUPPORTED |
以非事務方式執行操做,若是當前存在事務,就把當前事務掛起。 |
PROPAGATION_NEVER |
以非事務方式執行,若是當前存在事務,則拋出異常。 |
PROPAGATION_NESTED |
若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則執行與PROPAGATION_REQUIRED相似的操做。 |
當使用PROPAGATION_NESTED時,底層的數據源必須基於JDBC 3.0,而且實現者須要支持保存點事務機制。
幾種容易引發誤解的組合事務傳播行爲
當服務接口方法分別使用表1中不一樣的事務傳播行爲,且這些接口方法又發生相互調用的狀況下,大部分組合都是一目瞭然,容易理解的。可是,也存在一些容易引發誤解的組合事務傳播方式。
下面,咱們經過兩個具體的服務接口的組合調用行爲來破解這一難點。這兩個服務接口分別是UserService和 ForumService,UserSerice有一個addCredits()方法,ForumSerivce#addTopic()方法調用了 UserSerice#addCredits()方法,發生關聯性服務方法的調用:
public class ForumService {
private UserService userService;
public void addTopic(){①調用其它服務接口的方法
//add Topic…
userService.addCredits();②被關聯調用的業務方法
}
}
嵌套調用的事務方法
對Spring事務傳播行爲最多見的一個誤解是:當服務接口方法發生嵌套調用時,被調用的服務方法只能聲明爲 PROPAGATION_NESTED。這種觀點犯了望文生義的錯誤,誤認爲PROPAGATION_NESTED是專爲方法嵌套準備的。這種誤解遺害不 淺,執有這種誤解的開發者錯誤地認爲:應儘可能不讓Service類的業務方法發生相互的調用,Service類只能調用DAO層的DAO類,以免產生嵌 套事務。
其實,這種顧慮是徹底沒有必要的,PROPAGATION_REQUIRED已經清楚地告訴咱們:事務的方法會足夠「聰明」地判斷上下文是否已經存在一個事務中,若是已經存在,就加入到這個事務中,不然建立一個新的事務。
依照上面的例子,假設咱們將ForumService#addTopic()和UserSerice#addCredits()方法的事務傳播行爲都設置爲PROPAGATION_REQUIRED,這兩個方法將運行於同一個事務中。
爲了清楚地說明這點,能夠將Log4J的日誌設置爲DEBUG級別,以觀察Spring事務管理器內部的運行狀況。下面將兩個業務方法都設置爲PROPAGATION_REQUIRED,Spring所輸出的日誌信息以下:
Using transaction object
[org.springframework.jdbc.datasource.DataSourceTransactionManager$DataSourceTransactionObject@e3849c]
①爲ForumService#addTopic()新建一個事務
Creating new transaction with name [com.baobaotao.service.ForumService.addTopic]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Acquired Connection [org.apache.commons.dbcp.PoolableConnection@dc41c5] for JDBC transaction
Switching JDBC Connection [org.apache.commons.dbcp.PoolableConnection@dc41c5] to manual commit
Bound value [org.springframework.jdbc.datasource.ConnectionHolder@ee1ede] for key [org.apache.commons.dbcp.BasicDataSource@4204] to thread [main]
Initializing transaction synchronization
Getting transaction for [com.baobaotao.service.ForumService.addTopic]
Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@ee1ede] for key [org.apache.commons.dbcp.BasicDataSource@4204] bound to thread [main]
Using transaction object [org.springframework.jdbc.datasource.DataSourceTransactionManager$DataSourceTransactionObject@8b8a47]
②UserService#addCredits()簡單地加入到已存在的事務中(即①處建立的事務)
Participating in existing transaction
Getting transaction for [com.baobaotao.service.UserService.addCredits]
Completing transaction for [com.baobaotao.service.UserService.addCredits]
Completing transaction for [com.baobaotao.service.ForumService.addTopic]
Triggering beforeCommit synchronization
Triggering beforeCompletion synchronization
Initiating transaction commit
③調用底層Connection#commit()方法提交事務
Committing JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@dc41c5]
Triggering afterCommit synchronization
Triggering afterCompletion synchronization
Clearing transaction synchronization
嵌套事務
將ForumService#addTopic()設置爲PROPAGATION_REQUIRED 時,UserSerice#addCredits()設置爲PROPAGATION_REQUIRED、PROPAGATION_SUPPORTS、 PROPAGATION_MANDATORY時,運行的效果都是一致的(固然,若是單獨調用addCredits()就另當別論了)。
當addTopic()運行在一個事務下(如設置爲PROPAGATION_REQUIRED),而addCredits()設置爲 PROPAGATION_NESTED時,若是底層數據源支持保存點,Spring將爲內部的addCredits()方法產生的一個內嵌的事務。若是 addCredits()對應的內嵌事務執行失敗,事務將回滾到addCredits()方法執行前的點,並不會將整個事務回滾。內嵌事務是內層事務的一 部分,因此只有外層事務提交時,嵌套事務才能一併提交。
嵌套事務不可以提交,它必須經過外層事務來完成提交的動做,外層事務的回滾也會形成內部事務的回滾。
嵌套事務和新事務
PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED也是容易混淆的兩個傳播行爲。PROPAGATION_REQUIRES_NEW 啓動一個新的、和外層事務無關的「內部」事務。該事務擁有本身的獨立隔離級別和鎖,不依賴於外部事務,獨立地提交和回滾。當內部事務開始執行時,外部事務 將被掛起,內務事務結束時,外部事務才繼續執行。
因而可知, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大區別在於:PROPAGATION_REQUIRES_NEW 將建立一個全新的事務,它和外層事務沒有任何關係,而 PROPAGATION_NESTED 將建立一個依賴於外層事務的子事務,當外層事務提交或回滾時,子事務也會連帶提交和回滾。
其它須要注意問題
如下幾個問題值得注意:
1) 當業務方法被設置爲PROPAGATION_MANDATORY時,它就不能被非事務的業務方法調用。如將 ForumService#addTopic()設置爲PROPAGATION_MANDATORY,若是展示層的Action直接調用 addTopic()方法,將引起一個異常。正確的狀況是:addTopic()方法必須被另外一個帶事務的業務方法調用(如 ForumService#otherMethod())。因此PROPAGATION_MANDATORY的方法通常都是被其它業務方法間接調用的。
2) 當業務方法被設置爲PROPAGATION_NEVER時,它將不能被擁有事務的其它業務方法調用。假設 UserService#addCredits()設置爲PROPAGATION_NEVER,當ForumService# addTopic()擁有一個事務時,addCredits()方法將拋出異常。因此PROPAGATION_NEVER方法通常是被直接調用的。
3)當方法被設置爲PROPAGATION_NOT_SUPPORTED時,外層業務方法的事務會被掛起,當內部方法運行完成後,外層方法的事務從新運行。若是外層方法沒有事務,直接運行,不須要作任何其它的事。
小結
在Spring聲明式事務管理的配置中,事務傳播行爲是最容易被誤解的配置項,緣由在於事務傳播行爲名稱(如 PROPAGATION_NESTED:嵌套式事務)和代碼結構的相似性上(業務類方法嵌套調用另外一個業務類方法)。這種誤解在不少Spring開發者中 普遍存在,本文深刻講解了Spring事務傳播行爲對業務方法嵌套調用的真實影響,但願能幫助讀者化解對事務傳播行爲的困惑。