Spring事務註解@Transactional的坑爹陷阱

生產系統核心部分特別是涉及到賬戶的批量處理,放在凌晨定時運行,結果出問題了,現象很詭異:

1)問題偶發,但沒有規律

2)程序沒有按照預定步驟處理數據庫相關數據

3)發生後也沒有發現有死鎖,但出現異常的鎖

前後折騰了三次,但一直沒有找到根源。

後來另外一個問題發生時,分析發現數據庫事務處理形同虛設,用Spring @Transactional註解的數據庫事務完全沒有生效,讓人費解,甚至懷疑所用的PostgreSQL數據庫是不是有問題,在事務控制方面是不是有Bug。

後來查詢資料,發現是對Spring @Transactional事務註解理解不當,使用有誤造成的。坑爹的陷阱,讓團隊加了好幾次班

由於Spring是通過AOP方式實現@Transactional事務註解的,由於AOP的侷限性,在一些特定的情況下,@Transactional註解不會生效,直接導致數據庫的併發控制出現問題。

以下是一篇關於Spring @Transactional註解失效的一篇文章,文章還提供了一個工具類,用來調試判斷當前事務是否開啓。

http://blog.timmattison.com/archives/2012/04/19/tips-for-debugging-springs-transactional-annotation/

文章的要點如下:

  1. @Transactional annotations only work on public methods. If you have a private or protected method with this annotation there’s no (easy) way for Spring AOP to see the annotation. It doesn’t go crazy trying to find them so make sure all of your annotated methods are public.

  2. Transaction boundaries are only created when properly annotated (see above) methods are called through a Spring proxy. This means that you need to call your annotated method directly through an @Autowired bean or the transaction will never start. If you call a method on an @Autowired bean that isn’t annotated which itself calls a public method that is annotatedYOUR ANNOTATION IS IGNORED. This is because Spring AOP is only checking annotations when it first enters the @Autowired code.

  3. Never blindly trust that your @Transactional annotations are actually creating transaction boundaries. When in doubt test whether a transaction really is active (see below)

另有一篇中文文章也闡述了類似問題和觀點
http://blog.itpub.net/273449/viewspace-1057345/
@Transactional spring 配置事務 注意事項

  1. 在需要事務管理的地方加@Transactional 註解。@Transactional 註解可以被應用於接口定義和接口方法、類定義和類的 public 方法上。

  2. @Transactional 註解只能應用到 public 可見度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 註解,它也不會報錯, 但是這個被註解的方法將不會展示已配置的事務設置。

  3. 注意僅僅 @Transactional 註解的出現不足於開啓事務行爲,它僅僅 是一種元數據。必須在配置文件中使用配置元素,才真正開啓了事務行爲。

  4. 通過 元素的 “proxy-target-class” 屬性值來控制是基於接口的還是基於類的代理被創建。如果 “proxy-target-class” 屬值被設置爲 “true”,那麼基於類的代理將起作用(這時需要CGLIB庫cglib.jar在CLASSPATH中)。如果 “proxy-target-class” 屬值被設置爲 “false” 或者這個屬性被省略,那麼標準的JDK基於接口的代理將起作用。


原文:https://blog.csdn.net/hardywang/article/details/51206511

可以打在私有方法上,但是沒有意義,報紅的原因應該是idea設置的校驗問題
transactional標籤用於將對應包裝的bean設置成一個新的代理bean對象供外部使用,就是說外部調用這個proxy bean的公共方法時先會調用開啓事務等的切面工作,若設置成私有方法只能類內用this指針調用,這樣被調用的bean是其本身,不是proxy對象,因此沒有transactional切面的意義

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章