spring支持編程式事務管理和聲明式事務管理兩種方式:
1.編程式事務管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。對於編程式事務管理,spring推薦使用TransactionTemplate。spring
2.聲明式事務管理創建在AOP之上的。其本質是對方法先後進行攔截,而後在目標方法開始以前建立或者加入一個事務,在執行完目標方法以後根據執行狀況提交或者回滾事務。聲明式事務最大的優勢就是不須要經過編程的方式管理事務,這樣就不須要在業務邏輯代碼中摻瑣事務管理的代碼,只需在配置文件中作相關的事務規則聲明(或經過基於@Transactional註解的方式),即可以將事務規則應用到業務邏輯中。顯然聲明式事務管理要優於編程式事務管理,這正是spring倡導的非侵入式的開發方式。聲明式事務管理使業務代碼不受污染,一個普通的POJO對象,只要加上註解就能夠得到徹底的事務支持。和編程式事務相比,聲明式事務惟一不足地方是,後者的最細粒度只能做用到方法級別,沒法作到像編程式事務那樣能夠做用到代碼塊級別。可是即使有這樣的需求,也存在不少變通的方法,好比,能夠將須要進行事務管理的代碼塊獨立爲方法等等。數據庫
3.聲明式事務管理也有兩種經常使用的方式,一種是基於tx和aop名字空間的xml配置文件,另外一種就是基於@Transactional註解。顯然基於註解的方式更簡單易用,更清爽。編程
隔離級別是指若干個併發的事務之間的隔離程度。TransactionDefinition 接口中定義了五個表示隔離級別的常量:
1.TransactionDefinition.ISOLATION_DEFAULT:這是默認值,表示使用底層數據庫的默認隔離級別。對大部分數據庫而言,一般這值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
2.TransactionDefinition.ISOLATION_READ_UNCOMMITTED:該隔離級別表示一個事務能夠讀取另外一個事務修改但尚未提交的數據。該級別不能防止髒讀,不可重複讀和幻讀,所以不多使用該隔離級別。好比PostgreSQL實際上並無此級別。
3.TransactionDefinition.ISOLATION_READ_COMMITTED:該隔離級別表示一個事務只能讀取另外一個事務已經提交的數據。該級別能夠防止髒讀,這也是大多數狀況下的推薦值。
4.TransactionDefinition.ISOLATION_REPEATABLE_READ:該隔離級別表示一個事務在整個過程當中能夠屢次重複執行某個查詢,而且每次返回的記錄都相同。該級別能夠防止髒讀和不可重複讀。
5.TransactionDefinition.ISOLATION_SERIALIZABLE:全部的事務依次逐個執行,這樣事務之間就徹底不可能產生干擾,也就是說,該級別能夠防止髒讀、不可重複讀以及幻讀。可是這將嚴重影響程序的性能。一般狀況下也不會用到該級別。
數組
所謂事務的傳播行爲是指,若是在開始當前事務以前,一個事務上下文已經存在,此時有若干選項能夠指定一個事務性方法的執行行爲。在TransactionDefinition定義中包括了以下幾個表示傳播行爲的常量:
1.TransactionDefinition.PROPAGATION_REQUIRED:若是當前存在事務,則加入該事務;若是當前沒有事務,則建立一個新的事務。這是默認值。
2.TransactionDefinition.PROPAGATION_REQUIRES_NEW:建立一個新的事務,若是當前存在事務,則把當前事務掛起。
3.TransactionDefinition.PROPAGATION_SUPPORTS:若是當前存在事務,則加入該事務;若是當前沒有事務,則以非事務的方式繼續運行。
4.TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,若是當前存在事務,則把當前事務掛起。
5.TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,若是當前存在事務,則拋出異常。
6.TransactionDefinition.PROPAGATION_MANDATORY:若是當前存在事務,則加入該事務;若是當前沒有事務,則拋出異常。
7.TransactionDefinition.PROPAGATION_NESTED:若是當前存在事務,則建立一個事務做爲當前事務的嵌套事務來運行;若是當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。
併發
默認配置下,spring只有在拋出的異常爲運行時unchecked異常時纔回滾該事務,也就是拋出的異常爲RuntimeException的子類(Errors也會致使事務回滾),而拋出checked異常則不會致使事務回滾。能夠明確的配置在拋出那些異常時回滾事務,包括checked異常。也能夠明肯定義那些異常拋出時不回滾事務。還能夠編程性的經過setRollbackOnly()方法來指示一個事務必須回滾,在調用完setRollbackOnly()後你所能執行的惟一操做就是回滾。也就是說若是你try..catch..後沒有拋出異常或者手動回滾事務,事務就不會回滾了喔。 app
注意:若是事務配置爲 @Transactional(propagation = Propagation.NOT_SUPPORTED),這時候事務是不生效的,固然也不會發生數據回滾 性能
屬性 | 類型 | 描述 |
---|---|---|
value | String | 可選的限定描述符,指定使用的事務管理器 |
propagation | enum: Propagation | 可選的事務傳播行爲設置 |
isolation | enum: Isolation | 可選的事務隔離級別設置 |
readOnly | boolean | 讀寫或只讀事務,默認讀寫 |
timeout | int (in seconds granularity) | 事務超時時間設置 |
rollbackFor | Class對象數組,必須繼承自Throwable | 致使事務回滾的異常類數組 |
rollbackForClassName | 類名數組,必須繼承自Throwable | 致使事務回滾的異常類名字數組 |
noRollbackFor | Class對象數組,必須繼承自Throwable | 不會致使事務回滾的異常類數組 |
noRollbackForClassName | 類名數組,必須繼承自Throwable | 不會致使事務回滾的異常類名字數組 |
@Transactional 能夠做用於接口、接口方法、類以及類方法上。看成用於類上時,該類的全部 public 方法將都具備該類型的事務屬性,同時,咱們也能夠在方法級別使用該標註來覆蓋類級別的定義。@Transactional 註解能夠做用於接口、接口方法、類以及類方法上,可是 Spring 建議不要在接口或者接口方法上使用該註解,由於這隻有在使用基於接口的代理時它纔會生效。另外, @Transactional 註解應該只被應用到 public 方法上,這是由 Spring AOP 的本質決定的。若是你在 protected、private 或者默承認見性的方法上使用 @Transactional 註解,這將被忽略,也不會拋出任何異常。學習
我曾在一個類裏不一樣的方法間進行事務的調試,後來從相關文檔獲知:
默認狀況下,只有來自外部的方法調用纔會被AOP代理捕獲,也就是,類內部方法調用本類內部的其餘方法並不會引發事務行爲,即便被調用方法使用@Transactional註解進行修飾。
測試
下面是我在學習spring事務時候的練習代碼: serviceA.transactionalTest(): @Transactional(propagation = Propagation.REQUIRED) public void transactionalTest() { try { positionsService.transactionalTest1(); }catch (Exception e){ System.out.println(" positionsService.transactionalTest1()方法出錯了"); } try { departmentService.transactionalTest2(); }catch (Exception e){ System.out.println("departmentService.transactionalTest2(方法出錯了"); } } serviceB.transactionalTest1(): @Transactional(propagation = Propagation.REQUIRED) public void transactionalTest1() { Positions positions=positionsMapper.selectByPrimaryKey(1); positions.setPositionname("兒科主任醫生123"); positionsMapper.updateByPrimaryKeySelective(positions); } serviceC.transactionalTest2(): @Transactional(propagation = Propagation.REQUIRED) public void transactionalTest2() { Department department = departmentMapper.selectByPrimaryKey(1); department.setDepartmentname("兒科123"); departmentMapper.updateByPrimaryKeySelective(department); System.out.println(1/0); }
狀況一:三個事務的行爲:propagation = Propagation.REQUIRED:
根據propagation = Propagation.REQUIRED的屬性定義,transactionalTest、transactionalTest一、transactionalTest2都是來自同一個事務,只要有一個事務出錯,transactionalTest()中不管捕獲仍是未捕獲異常,全部事務都會回滾。由於在內層已經拋出了異常且被事務處理器捕獲到了。可是若是在transactionalTest2()中加入了try..catch..捕獲了該異常,那麼事務就會正常提交。因此若是內層捕獲了異常且沒有拋出異常,事務就不會回滾。若是內層沒有捕獲異常,不管外層有沒有捕獲異常,事務都會回滾。spa
狀況二:transactionalTest()和transactionalTest1()的事務行爲:propagation = Propagation.REQUIRED,transactionalTest2()的行爲:propagation = Propagation.NESTED:
transactionalTest2()是一個單獨的事務,transactionalTest1()和transactionalTest()是屬於同個事務。若是transactionalTest2()方法出錯,transactionalTest2()沒有try..catch..去捕獲,transactionalTest()中try..catch..去捕獲異常,最終異常會在transactionalTest()被捕獲,這沒有影響外層事務,因此transactionalTest1()事務成功執行。若是transactionalTest2()方法出錯,transactionalTest2()沒有try..catch..去捕獲,且在transactionalTest()也沒有try..catch..去捕獲異常,異常從內層拋到外層,致使內層事務和外層事務都捕獲了異常,這就影響了外層事務,因此transactionalTest1()事務回滾。若是transactionalTest2()方法出錯,transactionalTest2()內try..catch..去捕獲異常,transactionalTest()中不管有沒有try..catch..去捕獲異常,都不會影響外層事務,因此transactionalTest1()和transactionalTest2()都會成功執行。若是transactionalTest()方法出錯, transactionalTest1()和transactionalTest2()正常,由於transactionalTest()的所屬事務是主事務,因此會致使主事務和子事務都發生回滾。
狀況三:transactionalTest()和transactionalTest1()的事務行爲:propagation = Propagation.REQUIRED,transactionalTest2()的行爲:propagation = Propagation.NOT_SUPPORTED:
transactionalTest1()和transactionalTest()是屬於同個事務,transactionalTest2()非事務運行。若是transactionalTest()或transactionalTest1()方法出錯,且沒有去try..catch..去捕獲異常,那麼事務回滾,可是這不影響transactionalTest2()的提交。因此無論主事務有沒有回滾都不影響當前的方法執行。若是transactionalTest2()方法出錯,因爲沒有事務,因此不會回滾,可是主事務捕獲到異常後會發生回滾
狀況四:transactionalTest()和transactionalTest2()的事務行爲:propagation = Propagation.REQUIRED,transactionalTest1()的行爲:propagation = Propagation.REQUIRES_NEW:
transactionalTest()和transactionalTest2()是屬於同個事務,transactionalTest1()開啓新的事務。若是transactionalTest()沒有對transactionalTest1()和transactionalTest2()進行try..catch..的異常捕獲,此時transactionalTest2()方法出錯,致使主事務回滾,可是transactionalTest1()是新的事務,不影響它的提交。若是transactionalTest2()內捕獲了異常,主事務就不會回滾。
建議初學者對着電腦把代碼敲一遍,好好了解一下spring事務的傳播行爲,遇到不懂得能夠再帶着問題去查詢相關資料。
參考文檔:https://blog.csdn.net/bao19901210/article/details/41724355