@Transactional事務幾點注意

這裏面有幾點須要你們留意:
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,事務不會回滾。java

 

 

 

 

 

在同一個類中,一個方法調用另一個有註解(好比@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. 把註解加到類名上面;
相關文章
相關標籤/搜索