javascript
Spring事务管理嵌套事务详解 : 同一个类中,一个方法调用另外一个有事务的方法
Spring 事務機制回顧
?? Spring事務一個被訛傳很廣說法是:一個事務方法不應該調用另一個事務方法,否則將產生兩個事務.? 結果造成開發人員在設計事務方法時束手束腳,生怕一不小心就踩到地雷。?
? 其實這是不認識Spring事務傳播機制而造成的誤解,Spring對事務控制的支持統一在TransactionDefinition類中描述,該類有以下? ?幾個重要的接口方法:
除了事務的傳播行為外,事務的其他特性Spring是借助底層資源的功能來完成的,Spring無非只充當個代理的角色。但是事務的傳播行為卻是Spring憑借自身的框架提供的功能 ;
Spring事務傳播屬性:
? ?所謂事務傳播行為就是多個事務方法相互調用時,事務如何在這些方法間傳播。Spring支持以下7種事務傳播行為
Spring事務傳播屬性:
1.propagation-required: 支持當前事務,如果有就加入當前事務中;如果當前方法沒有事務,就新建一個事務;
2.propagation-supports: 支持當前事務,如果有就加入當前事務中;如果當前方法沒有事務,就以非事務的方式執行;
3.propagation-mandatory: 支持當前事務,如果有就加入當前事務中;如果當前沒有事務,就拋出異常;
4.propagation-requires_new: 新建事務,如果當前存在事務,就把當前事務掛起;如果當前方法沒有事務,就新建事務;
5.propagation-not-supported: 以非事務方式執行,如果當前方法存在事務就掛起當前事務;如果當前方法不存在事務,就以非事務方式執行;
6.propagation-never: 以非事務方式執行,如果當前方法存在事務就拋出異常;如果當前方法不存在事務,就以非事務方式執行;
7.propagation-nested: 如果當前方法有事務,則在嵌套事務內執行;如果當前方法沒有事務,則與required操作類似;
前六個策略類似于EJB CMT,第七個(PROPAGATION_NESTED)是Spring所提供的一個特殊變量。
它要求事務管理器或者使用JDBC 3.0 Savepoint API提供嵌套事務行為(如Spring的DataSourceTransactionManager)
在同一個類中,一個方法調用另外一個有注解(比如@Async,@Transational)的方法,注解是不會生效的
代碼示例:
?例子中,有兩方法,一個有@Transational注解,一個沒有。如果調用了有注解的addPerson()方法,會啟動一個Transaction;如果調用updatePersonByPhoneNo(),因為它內部調用了有注解的addPerson(),如果你以為系統也會為它啟動一個Transaction,那就錯了,實際上是沒有的?
@Service public class PersonServiceImpl implements PersonService {@AutowiredPersonDao personDao;@Override@Transactionalpublic boolean addPerson(Person person) {boolean result = personDao.insertPerson(person)>0 ? true : false;return result;}@Override//@Transactionalpublic boolean updatePersonByPhoneNo(Person person) {boolean result = personDao.updatePersonByPhoneNo(person)>0 ? true : false;addPerson(person); //測試同一個類中@Transactional是否起作用return result;} }原因:
spring 在掃描bean的時候會掃描方法上是否包含@Transactional注解,如果包含,spring會為這個bean動態地生成一個子類(即代理類,proxy),代理類是繼承原來那個bean的。此時,當這個有注解的方法被調用的時候,實際上是由代理類來調用的,代理類在調用之前就會啟動transaction。然而,如果這個有注解的方法是被同一個類中的其他方法調用的,那么該方法的調用并沒有通過代理類,而是直接通過原來的那個bean,所以就不會啟動transaction,我們看到的現象就是@Transactional注解無效。
?
?
為什么一個方法a()調用同一個類中另外一個方法b()的時候,b()不是通過代理類來調用的呢?可以看下面的例子(為了簡化,用偽代碼表示):
@Service class A{@Transactinalmethod b(){...}method a(){ //標記1b();} }//Spring掃描注解后,創建了另外一個代理類,并為有注解的方法插入一個startTransaction()方法: class proxy$A{A objectA = new A();method b(){ //標記2startTransaction();objectA.b();}method a(){ //標記3objectA.a(); //由于a()沒有注解,所以不會啟動transaction,而是直接調用A的實例的a()方法} }當我們調用A的bean的a()方法的時候,也是被proxy$A攔截,執行proxy$A.a()(標記3),然而,由以上代碼可知,這時候它調用的是objectA.a(),也就是由原來的bean來調用a()方法了,所以代碼跑到了“標記1”。由此可見,“標記2”并沒有被執行到,所以startTransaction()方法也沒有運行。
?
解決的方法就簡單了(兩種):
結論:
在一個Service內部,事務方法之間的嵌套調用,普通方法和事務方法之間的嵌套調用,都不會開啟新的事務.
1. spring采用動態代理機制來實現事務控制,而動態代理最終都是要調用原始對象的,而原始對象在去調用方法時,是不會再觸發代理了!
2. Spring的事務管理是通過AOP實現的,其AOP的實現對于非final類是通過cglib這種方式,即生成當前類的一個子類作為代理類,然后在調用其下的方法時,會判斷這個方法有沒有@Transactional注解,如果有的話,則通過動態代理實現事務管理(攔截方法調用,執行事務等切面)。當b()中調用a()時,發現b()上并沒有@Transactional注解,所以整個AOP代理過程(事務管理)不會發生。
附?使用AOP?代理后的方法調用執行流程:
總結
以上是生活随笔為你收集整理的Spring事务管理嵌套事务详解 : 同一个类中,一个方法调用另外一个有事务的方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 0076-小升初1:生日蛋糕
- 下一篇: 分享功能 集成友盟分享