一进宝、癥狀描述
spring 同一個 service 中? 未開啟 事務(wù)的方法 A 調(diào)用 本類中開發(fā)了 事務(wù)的方法B抵知, 整體事務(wù)失效
二宇整、解決過程
spring事務(wù)的傳播機制及原因分析;
PROPAGATION_REQUIRED -- 支持當(dāng)前事務(wù)吗跋,如果當(dāng)前沒有事務(wù)嚼沿,就新建一個事務(wù)宣脉。這是最常見的選擇车柠。
PROPAGATION_SUPPORTS -- 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù)塑猖,就以非事務(wù)方式執(zhí)行竹祷。
PROPAGATION_MANDATORY -- 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù)萌庆,就拋出異常溶褪。
PROPAGATION_REQUIRES_NEW -- 新建事務(wù),如果當(dāng)前存在事務(wù)践险,把當(dāng)前事務(wù)掛起猿妈。
PROPAGATION_NOT_SUPPORTED -- 以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù)巍虫,就把當(dāng)前事務(wù)掛起彭则。
PROPAGATION_NEVER -- 以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù)占遥,則拋出異常俯抖。
PROPAGATION_NESTED -- 如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行瓦胎。如果當(dāng)前沒有事務(wù)芬萍,則進行與PROPAGATION_REQUIRED類似的操作。
spring默認的是PROPAGATION_REQUIRED機制,如果方法A標注了注解@Transactional 是完全沒問題的,執(zhí)行的時候傳播給方法B,因為方法A開啟了事務(wù),線程內(nèi)的connection的屬性autoCommit=false,并且執(zhí)行到方法B時,事務(wù)傳播依然是生效的,得到的還是方法A的connection,autoCommit還是為false,所以事務(wù)生效;反之,如果方法A沒有注解@Transactional 時,是不受事務(wù)管理的,autoCommit=true,那么傳播給方法B的也為true,執(zhí)行完自動提交,即使B標注了@Transactional ;
在一個Service內(nèi)部搔啊,事務(wù)方法之間的嵌套調(diào)用柬祠,普通方法和事務(wù)方法之間的嵌套調(diào)用,都不會開啟新的事務(wù).是因為spring采用動態(tài)代理機制來實現(xiàn)事務(wù)控制负芋,而動態(tài)代理最終都是要調(diào)用原始對象的漫蛔,而原始對象在去調(diào)用方法時,是不會再觸發(fā)代理了!
所以以上就是為什么我在沒有標注事務(wù)注解的方法A里去調(diào)用標注有事務(wù)注解的方法B而沒有事務(wù)滾回的原因;
解決方案:
? 1.把方法B抽離到另外一個XXService中去,并且在這個Service中注入XXService,使用XXService調(diào)用方法B;
? ? ? 顯然,這種方式一點也不優(yōu)雅,且要產(chǎn)生很多冗余文件,看起來很煩,實際開發(fā)中也幾乎沒人這么做吧?.反正我不建議采用此方案;
? 2.通過在方法內(nèi)部獲得當(dāng)前類代理對象的方式,通過代理對象調(diào)用方法B
? 上面說了:動態(tài)代理最終都是要調(diào)用原始對象的莽龟,而原始對象在去調(diào)用方法時蠕嫁,是不會再觸發(fā)代理了!
? ? 所以我們就使用代理對象來調(diào)用,就會觸發(fā)事務(wù);
綜上解決方案,我覺得第二種方式簡直方便到炸. 那怎么獲取代理對象呢? 這里提供兩種方式:
? 1.使用 ApplicationContext 上下文對象獲取該對象;
? 2.使用 AopContext.currentProxy() 獲取代理對象,但是需要配置exposeProxy=true