天天看點

spring @Transactional 事務注解

spring @Transactional 事務注解

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class)
簡單解析:如果有事務,那麼加入事務,沒有的話建立一個; 串行化最進階隔離級别; 遇到異常復原。      
spring @Transactional 事務注解

@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兩種不同的傳播機制,功能類似,都涉及到了事務嵌套的問題,那兩者有何差別呢?該如何正确使用這兩種模式呢?

        以下是摘自Spring的文檔:

          PROPAGATION_REQUIRES_NEW : uses a completely independent transaction for each affected transaction scope. In that case, the underlying physical transactions are different and hence can commit or roll back independently, with an outer transaction not affected by an inner transaction's rollback status.

         内部的事務獨立運作,在各自的作用域中,可以獨立的復原或者送出;而外部的事務将不受内部事務的復原狀态影響。 

        ROPAGATION_NESTED : uses a single physical transaction with multiple savepoints that it can roll back to. Such partial rollbacks allow an inner transaction scope to trigger a rollback for its scope, with the outer transaction being able to continue the physical transaction despite some operations having been rolled back. This setting is typically mapped onto JDBC savepoints, so will only work with JDBC resource transactions.

       NESTED的事務,基于單一的事務來管理,提供了多個儲存點。這種多個儲存點的機制允許内部事務的變更觸發外部事務的復原。而外部事務在混滾之後,仍能繼續進行事務處理,即使部分操作已經被混滾。 由于這個設定基于JDBC的儲存點,是以隻能工作在JDBC的機制智商。

       由此可知, 兩者都是事務嵌套,不同之處在于,内外事務之間是否存在彼此之間的影響;NESTED之間會受到影響,而産生部分復原,而REQUIRED_NEW則是獨立的。

 @Transactional之isolation(多使用與多使用者并發操作資料庫,注意會引發的并發問題)

@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 :串行化

具體如何來設定具體的隔離界别,則依據業務系統具體可以容忍的程度而定。Serializable最為嚴格,然而效率最低;Read Uncommited效率最高,但是容易出現各種問題,中間的2個級别介于二者之間,故本質上是效率與出錯機率的平衡與妥協。

@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意外就不復原。

       用來指明復原的條件是哪些異常類或者異常類名。用法比較簡單,這些不再贅述。

  • spring事務復原規則

訓示spring事務管理器復原一個事務的推薦方法是在目前事務的上下文内抛出異常。spring事務管理器會捕捉任何未處理的異常,然後依據規則決定是否復原抛出異常的事務。

預設配置下,spring隻有在抛出的異常為運作時unchecked異常時才復原該事務,也就是抛出的異常為RuntimeException的子類(Errors也會導緻事務復原),而抛出checked異常則不會導緻事務復原。

可以明确的配置在抛出那些異常時復原事務,包括checked異常。也可以明确定義那些異常抛出時不復原事務。

還可以程式設計性的通過setRollbackOnly()方法來訓示一個事務必須復原,在調用完setRollbackOnly()後你所能執行的唯一操作就是復原。

 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 的事務開啟 ,或者是基于接口的 或者是基于類的代理被建立。是以在同一個類中一個方法調用另一個方法有事務的方法,事務是不會起作用的。

繼續閱讀