spring事物

這裏面有幾點需要大家留意:
A. 一個功能是否要事務,必須納入設計、編碼考慮。不能僅僅完成了基本功能就ok。
B. 如果加了事務,必須做好開發環境測試(測試環境也儘量觸發異常、測試回滾),確保事務生效。
C. 以下列了事務使用過程的注意事項,請大家留意。
1. 不要在接口上聲明@Transactional ,而要在具體類的方法上使用 @Transactional 註解,否則註解可能無效。
2.不要圖省事,將@Transactional放置在類級的聲明中,放在類聲明,會使得所有方法都有事務。故@Transactional應該放在方法級別,不需要使用事務的方法,就不要放置事務,比如查詢方法。否則對性能是有影響的。
3.使用了@Transactional的方法,對同一個類裏面的方法調用, @Transactional無效。比如有一個類Test,它的一個方法A,A再調用Test本類的方法B(不管B是否public還是private),但A沒有聲明註解事務,而B有。則外部調用A之後,B的事務是不會起作用的。(經常在這裏出錯)
4.使用了@Transactional的方法,只能是public,@Transactional註解的方法都是被外部其他類調用纔有效,故只能是public。道理和上面的有關聯。故在 protected、private 或者 package-visible 的方法上使用 @Transactional 註解,它也不會報錯,但事務無效。
5.經過在ICORE-CLAIM中測試,效果如下:
A.拋出受查異常XXXException,事務會回滾。
B.拋出運行時異常NullPointerException,事務會回滾。
C.Quartz中,execute直接調用加了@Transactional方法,可以回滾;間接調用,不會回滾。(即上文3點提到的)
D.異步任務中,execute直接調用加了@Transactional方法,可以回滾;間接調用,不會回滾。(即上文3點提到的)
E.在action中加上@Transactional,不會回滾。切記不要在action中加上事務。
F.在service中加上@Transactional,如果是action直接調該方法,會回滾,如果是間接調,不會回滾。(即上文3提到的)
G.在service中的private加上@Transactional,事務不會回滾。

 

 

 

 

 

在同一個類中,一個方法調用另外一個有註解(比如@Async,@Transational)的方法,註解是不會生效的。

 

比如,下面代碼例子中,有兩方法,一個有@Transational註解,一個沒有。如果調用了有註解的addPerson()方法,會啓動一個Transaction;如果調用updatePersonByPhoneNo(),因爲它內部調用了有註解的addPerson(),如果你以爲系統也會爲它啓動一個Transaction,那就錯了,實際上是沒有的。

 

[java] view plain copy

 

  1. @Service  
  2. public class PersonServiceImpl implements PersonService {  
  3.   
  4.  @Autowired  
  5.  PersonDao personDao;  
  6.    
  7.  @Override  
  8.  @Transactional  
  9.  public boolean addPerson(Person person) {  
  10.   boolean result = personDao.insertPerson(person)>0 ? true : false;  
  11.   return result;  
  12.  }  
  13.   
  14.  @Override  
  15.  //@Transactional  
  16.  public boolean updatePersonByPhoneNo(Person person) {  
  17.   boolean result = personDao.updatePersonByPhoneNo(person)>0 ? true : false;  
  18.   addPerson(person); //測試同一個類中@Transactional是否起作用  
  19.   return result;  
  20.  }  
  21. }  

 

如何查看是否啓動了Transaction?

設置log leve爲debug,可以查看是否有下面這個log,判斷是否啓動了Transaction:

DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Creating new transaction with name...

 

同樣地,@Async等其他註解也有這樣的問題。

(關於@Async的用法,請參考:http://blog.csdn.net/clementad/article/details/47403185

 

原因:

spring 在掃描bean的時候會掃描方法上是否包含@Transactional註解,如果包含,spring會爲這個bean動態地生成一個子類(即代理類,proxy),代理類是繼承原來那個bean的。此時,當這個有註解的方法被調用的時候,實際上是由代理類來調用的,代理類在調用之前就會啓動transaction。然而,如果這個有註解的方法是被同一個類中的其他方法調用的,那麼該方法的調用並沒有通過代理類,而是直接通過原來的那個bean,所以就不會啓動transaction,我們看到的現象就是@Transactional註解無效。

 

爲什麼一個方法a()調用同一個類中另外一個方法b()的時候,b()不是通過代理類來調用的呢?可以看下面的例子(爲了簡化,用僞代碼表示):

 

[java] view plain copy

 

  1. @Service  
  2. class A{  
  3.     @Transactinal  
  4.     method b(){...}  
  5.       
  6.     method a(){    //標記1  
  7.         b();  
  8.     }  
  9. }  
  10.   
  11. //Spring掃描註解後,創建了另外一個代理類,併爲有註解的方法插入一個startTransaction()方法:  
  12. class proxy$A{  
  13.     A objectA = new A();  
  14.     method b(){    //標記2  
  15.         startTransaction();  
  16.         objectA.b();  
  17.     }  
  18.   
  19.     method a(){    //標記3  
  20.         objectA.a();    //由於a()沒有註解,所以不會啓動transaction,而是直接調用A的實例的a()方法  
  21.     }  
  22. }  


當我們調用A的bean的a()方法的時候,也是被proxy$A攔截,執行proxy$A.a()(標記3),然而,由以上代碼可知,這時候它調用的是objectA.a(),也就是由原來的bean來調用a()方法了,所以代碼跑到了“標記1”。由此可見,“標記2”並沒有被執行到,所以startTransaction()方法也沒有運行。

 

瞭解了失效的原因,解決的方法就簡單了(兩種):

    1. 把這兩個方法分開到不同的類中;
    2. 把註解加到類名上面;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章