@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class)
簡單解析:如果有事務,那么加入事務,沒有的話新建一個; 串行化最高級隔離級別; 遇到異常回滾。
@Transactional之value
value這里主要用來指定不同的事務管理器;主要用來滿足在同一個系統中,存在不同的事務管理器。比如在Spring中,聲明了兩種事務管理器txManager1, txManager2.
然后,用戶可以根據這個參數來根據需要指定特定的txManager.
@Transactional之propagation(service中調用其他service時需要注意)
@Transactional(propagation=Propagation.REQUIRED) | 如果有事務, 那么加入事務, 沒有的話新建一個(默認情況下) |
@Transactional(propagation=Propagation.NOT_SUPPORTED) | 容器不為這個方法開啟事務 |
@Transactional(propagation=Propagation.REQUIRES_NEW) | 不管是否存在事務,都創建一個新的事務,原來的掛起,新的執行完畢,繼續執行老的事務 |
@Transactional(propagation=Propagation.MANDATORY) | 必須在一個已有的事務中執行,否則拋出異常 |
@Transactional(propagation=Propagation.NEVER) | 必須在一個沒有的事務中執行,否則拋出異常(與Propagation.MANDATORY相反) |
@Transactional(propagation=Propagation.SUPPORTS) | 如果其他bean調用這個方法,在其他bean中聲明事務,那就用事務。如果其他bean沒有聲明事務,那就 |
Propagation支持7種不同的傳播機制:
- REQUIRED
業務方法需要在一個事務中運行,如果方法運行時,已處在一個事務中,那么就加入該事務,否則自己創建一個新的事務.這是spring默認的傳播行為.。
- SUPPORTS:
如果業務方法在某個事務范圍內被調用,則方法成為該事務的一部分,如果業務方法在事務范圍外被調用,則方法在沒有事務的環境下執行。
- MANDATORY:
只能在一個已存在事務中執行,業務方法不能發起自己的事務,如果業務方法在沒有事務的環境下調用,就拋異常
- REQUIRES_NEW
業務方法總是會為自己發起一個新的事務,如果方法已運行在一個事務中,則原有事務被掛起,新的事務被創建,直到方法結束,新事務才結束,原先的事務才會恢復執行.
- NOT_SUPPORTED
聲明方法需要事務,如果方法沒有關聯到一個事務,容器不會為它開啟事務.如果方法在一個事務中被調用,該事務會被掛起,在方法調用結束后,原先的事務便會恢復執行.
- NEVER:
聲明方法絕對不能在事務范圍內執行,如果方法在某個事務范圍內執行,容器就拋異常.只有沒關聯到事務,才正常執行.
- NESTED:
如果一個活動的事務存在,則運行在一個嵌套的事務中.如果沒有活動的事務,則按REQUIRED屬性執行.它使用了一個單獨的事務, 這個事務擁有多個可以回滾的保證點.內部事務回滾不會對外部事務造成影響, 它只對DataSourceTransactionManager 事務管理器起效.
其實大家最感到困惑的是REQUIRED_NEW和NESTED兩種不同的傳播機制,功能類似,都涉及到了事務嵌套的問題,那兩者有何區別呢?該如何正確使用這兩種模式呢?
@Transactional(isolation = Isolation.READ_UNCOMMITTED) | 讀取未提交數據(會出現臟讀, 不可重復讀) 基本不使用 |
@Transactional(isolation = Isolation.READ_COMMITTED)(SQLSERVER默認) | 讀取已提交數據(會出現不可重復讀和幻讀) |
@Transactional(isolation = Isolation.REPEATABLE_READ) | 可重復讀(會出現幻讀) |
@Transactional(isolation = Isolation.SERIALIZABLE) | 串行化 |
隔離級別所要解決的問題是在應用程序中,存在多個事務同時在運行之時,需要解決和處理好的問題。那首先來看看,一般會出現哪些問題呢?先來看看吧。
- 臟讀(dirty read)
- 不可重復讀(non-repeatable read)
- 幻讀 (phantom read)
下面來看看Spring中@Transactional中Isolation有具備的值:
- DEFAULT 使用各個數據庫默認的隔離級別
- Read Uncommited :讀未提交數據( 會出現臟讀,不可重復讀,幻讀 )
- Read Commited :讀已提交的數據(會出現不可重復讀,幻讀)
- Repeatable Read :可重復讀(會出現幻讀)
- Serializable :串行化
@Transactional之timeout
用於設置事務處理的時間長度,阻止可能出現的長時間的阻塞系統或者占用系統資源。單位為秒。如果超時設置事務回滾,並拋出TransactionTimedOutException異常。
Spring事務超時 = 事務開始時到最后一個Statement創建時時間 + 最后一個Statement的執行時超時時間(即其queryTimeout)。
關於事務超時時間的計算可以參考:http://jinnianshilongnian.iteye.com/blog/1986023
@Transactional之readOnly
默認情況下是false,可以顯示指定為true, 告訴程序該方法下使用的是只讀操作,如果進行其他非讀操作,則會跑出異常;這個緊緊適用於只有readOnly標識的情況下,當其與propagation機制同時使用之時,則會出現只讀設置被覆蓋的情況,比如在required的情況下。在使用 REQUIRED 傳播模式時,會拋出一個只讀連接異常。使用 JDBC 時是這樣。使用基於 ORM 的框架時,只讀標志只是對數據庫的一個提示,並且一條基於 ORM 框架的指令(本例中是 hibernate)將對象緩存的 flush 模式設置為 NEVER,表示在這個工作單元中,該對象緩存不應與數據庫同步。不過,REQUIRED 傳播模式會覆蓋所有這些內容,允許事務啟動並工作,就好像沒有設置只讀標志一樣。
具體的詳細內容可以參考: http://robinsoncrusoe.iteye.com/blog/825531
@Transactional之rollbackForClassName/rollbackFor
Spring默認情況下會對運行期例外(RunTimeException)進行事務回滾。這個例外是unchecked,如果遇到checked意外就不回滾。
用來指明回滾的條件是哪些異常類或者異常類名。用法比較簡單,這些不再贅述。
@Transactional之noRollbackForClassName/noRollbackFor
作用雷同於8, 用來指明在拋出特定異常的情況下,不進行數據庫的事務回滾操作。
- spring事務回滾規則
Spring @Transactional的注意事項
- @Transactional 注解應該只被應用到 public 可見度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不會報錯, 但是這個被注解的方法將不會展示已配置的事務設置。
- 用 spring 事務管理器,由spring來負責數據庫的打開,提交,回滾。默認遇到運行期異常(throw new RuntimeException("注釋");)會回滾,即遇到不受檢查(unchecked)的異常時回滾;而遇到需要捕獲的異常(throw new Exception("注釋");)不會回滾,即遇到受檢查的異常(就是非運行時拋出的異常,編譯器會檢查到的異常叫受檢查異常或說受檢查異常)時,需我們指定方式來讓事務回滾 要想所有異常都回滾,要加上 @Transactional( rollbackFor={Exception。class,其它異常}) 。
- @Transactional 注解可以被應用於接口定義和接口方法、類定義和類的 public 方法上。然而,請注意僅僅 @Transactional 注解的出現不足於開啟事務行為,它僅僅是一種元數據,能夠被可以識別 @Transactional 注解和上述的配置適當的具有事務行為的beans所使用。上面的例子中,其實正是 <tx:annotation-driven/>元素的出現 開啟了事務行為。
- Spring團隊的建議是你在具體的類(或類的方法)上使用 @Transactional 注解,而不要使用在類所要實現的任何接口上。你當然可以在接口上使用 @Transactional 注解,但是這將只能當你設置了基於接口的代理時它才生效。因為注解是不能繼承的,這就意味着如果你正在使用基於類的代理時,那么事務的設置將不能被基於類的代理所識別,而且對象也將不會被事務代理所包裝(將被確認為嚴重的)。因此,請接受Spring團隊的建議並且在具體的類上使用 @Transactional 注解。
- @Transactional 注解標識的方法,處理過程盡量的簡單。尤其是帶鎖的事務方法,能不放在事務里面的最好不要放在事務里面。可以將常規的數據庫查詢操作放在事務前面進行,而事務內進行增、刪、改、加鎖查詢等操作。
- @Transactional 的事務開啟 ,或者是基於接口的 或者是基於類的代理被創建。所以在同一個類中一個方法調用另一個方法有事務的方法,事務是不會起作用的。