前言
相信大家都遇到一種事務失效場景,那就是 Spring 自調用,就是在 Service 方法內,調用另一個加
@Transactional
注解的方法,發現事務失效,這時候你是怎么解決的呢?
事情回顧
那是一個我忘了天氣咋樣的下午,突然蹦出一個小紅點,嗯~ 挺着急的小紅點。
原來是事務失效了!
莫慌!莫慌!
最后小伙伴選擇了抽走,是我的工具類不香了么?
當然故事的結果是完美的,問題解決了。
事務
在開發中涉及到同時操作多個表的時候,要保證兩個操作要么一起成功,要么一起失敗,這時候就需要用到事務。
現在一般使用的都是基於 @Transactional
注解的聲明式事務。
而事務使用過程中有以下幾個注意事項:
- 事務只能應用到 public 方法上才會有效;
- 事務需要從外部調用,Spring 自調用會失效;
- 建議事務注解 @Transactional 一般添加在實現類上。
當然這幾句話不是說我的,人家官方文檔可是明確說明的!
這里可是說明了應僅將 @Transactional 注解應用於具有公開可見性的方法。如果對受 protected, private o或 package-visible 修飾的方法使用,則不會引發任何錯誤,但是被注解的方法不會顯示已配置的事務設置。
說白了,就是你用了,不會報錯,但是不生效!
至於建議加在實現類上,這個只是建議,不過如果加在接口類或接口方法上時,只有配置基於接口的代理才會生效。所以這塊還是老老實實的加在實現類或實現類方法上
吧。
因為代理模式只攔截通過代理傳入的外部方法調用,所以自調用事務是不生效的。
官方的解釋還是比較簡單明了的,雖然我看不懂,但是不影響我截圖。
那我還是再截一個吧……
實際使用
但是在開發中,小伙伴們往往會遇到這種情況!
本來自己寫的代碼就一坨坨的又臭又長,里面有各種驗簽、驗參、查詢、驗證等等,就想着來個事務,讓事務包裹的范圍最小,僅僅在同時更新的時候加上事務吧!
這么寫,咦~ IDEA 報錯了,好像不能 private
修飾,那我改成 public
。
很顯然事務是不生效的。
把更新的代碼放到又臭又長
的代碼里面,讓它變得更臭更長,然后用 @Transactional
注解一加。完美解決!
請放過那坨代碼吧!來看看下面的辦法。
解決方案 1
那我改成外部調用不就行了么?
再聲明一個 Service,把更新表的邏輯放過去。
我一般就喜歡使用這個辦法。
解決方案 2
使用編程式事務
,前面說了,使用聲明式事務
時,又這又那,我換一種總可以吧!
你看,我還把方法改成 private
修飾了,事務也生效。完美解決!
其實這個方法也很不錯哦!
解決方案 3
又想用注解,又想自調用怎么辦?
不過... 麻煩一點還是可以的。
咱們可以參考編程式事務
的方式,不就是不讓自調用么,我調外部方法,然后外部方法再給我調回來不就可以了。
@Component
public class TransactionalComponent {
public interface Cell {
void run() throws Exception;
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void required(Cell cell) throws Exception {
cell.run();
}
}
這樣的話不就可以通過 TransactionalComponent
調用了么,並且還可以使用 lambda
表達式。
當然基於這個版本也可以做一個迭代,就是使用靜態方法調用,不用每次都用 @Autowired
注入一次。
public class TransactionalUtils {
private static volatile TransactionalComponent transactionalComponent;
private static synchronized TransactionalComponent getTransactionalComponent() {
if (transactionalComponent == null) {
// 從容器中獲取 transactionalComponent
transactionalComponent = ApplicationContextUtils.getBean(TransactionalComponent.class);
}
return transactionalComponent;
}
public static void required(TransactionalComponent.Cell cell) throws Exception {
getTransactionalComponent().required(cell);
}
}
這樣通過工具類 TransactionalUtils
便可以直接調用靜態方法的方式執行事務操作。
總結
結束語
本文主要介紹為什么會遇到事務失效,以及事務失效的避免方式,同時提供了三種方式來解決自調用事務失效的問題。不足之處,歡迎指正。