??在Spring項目中,我們經(jīng)常使用注解@Transactional來標(biāo)識一個事務(wù)方法。但是,有時會發(fā)現(xiàn)這個事務(wù)并不是按照我們想要的方式執(zhí)行。通常在以下幾種情況下,你以為的事務(wù)管理可能并不是你認(rèn)為的事務(wù)管理。

??在業(yè)務(wù)方法中使用try catch來捕獲了異常,然后異常出現(xiàn),會進(jìn)入到catch塊中,但是沒有進(jìn)行拋出,結(jié)果事務(wù)沒有被正確的回滾。這里沒有正確回滾的原因就在于,沒有正確的理解Spring事務(wù)管理的原理。Spring采用了代理的方式來實現(xiàn)事務(wù)管理,調(diào)用的順序是開啟事務(wù),執(zhí)行目標(biāo)方法,提交或回滾事務(wù)。所以雖然目標(biāo)方法中出現(xiàn)了異常,但是卻將異常直接處理了,在代理類眼中,目標(biāo)方法沒有拋出異常,所以事務(wù)也就正常提交。
@Transactional(rollbackFor = Exception.class)
public void transfer(int amount) {
try{
serviceB.sub(amount);
serviceA.add(amount);
} catch (Exception e) {
LOGGER.error("error occur");
}
}??正確的解決方法有兩種,一種是直接在catch中繼續(xù)拋出異常,第二種就是直接告訴Spring當(dāng)前事務(wù)需要rollback。
// 1
throw new RuntimeException(e);
// 2
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();拋出檢查異常??在方法中若是拋出檢查異常,比如fileNotFound這種,事務(wù)則是不會回滾的,這其中的原因就在于@Transactional 注解默認(rèn)的是rollbackFor是運行期異常。這也就是在阿里的開發(fā)規(guī)范中要求一定要指定rollbackFor的原因。
所以在使用這個注解的時候還是建議寫明 rollbackFor 這樣才能明確知道出現(xiàn)了什么異常才會回滾的。
@Transactional(rollbackFor = Exception.class)錯誤添加切面??Aop切面順序?qū)е率聞?wù)不能正確回滾,原因是事務(wù)切面優(yōu)先級最低,但如果自定義的切面優(yōu)先級和它一樣,則還是自定義切面在內(nèi)層,這是若自定義切面沒有正確拋出異常,在catch中吃掉了異常,此時就會出現(xiàn)和第一種情況類似的情況,代理類得不到異常信息,也就不會回滾。
??解決方案就是在切面中拋出異常,或者就是將自定義的切面的優(yōu)先級設(shè)置為更小。但是建議使用第一種在切面中拋出異常。
??非public的方法會導(dǎo)致事務(wù)失效。Spring為方法創(chuàng)建代理,添加事務(wù)通知,前提條件是該方法是public的,這點需要注意的就是要么設(shè)置方法為public,要么可以為非public的方法添加事務(wù)通知。
建議使用public方法而不是添加配置
@Bean
public TransactionAttributeSource transactionAttributeSource(){
return new AnnotationTransactionAttributeSource(false);
}調(diào)用本類方法導(dǎo)致傳播行為失效??同一個Service的兩個方法之間調(diào)用,就會出現(xiàn)這個問題,原因還是在代理對象這里,我們期待的調(diào)用時一個代理類的調(diào)用,但是我們?nèi)羰侵苯釉诜椒ㄖ袃?nèi)部調(diào)用,則被調(diào)用的方法的事務(wù)失效,沒有被Aop增強(qiáng)。
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void a (){
b();
}
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void b (){
}??以上的改進(jìn)方案就是自己調(diào)用自己,自己注入自己。你可能看見過這樣的代碼,就是為了解決這個問題的。這種解決方案最常見。
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES)
public void a (){
service.b();
}??還有一種方法可以通過 AopContext 拿到代理對象,然后再調(diào)用。這里要注意,使用這種方式需要開啟暴露代理。
@EnableAspectJAutoProxy(exposeProxy = true)
public class Application {}
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void a (){
((TestService)AopContext.currentProxy()).b();
}Transactional并沒有保證原子性??這個問題非常常見,我們總會以為加了事務(wù)管理,尤其是加了 Propagation.REQUIRES_NEW 之后我們的事務(wù)就會在方法執(zhí)行之后提交事務(wù),或是加了 synchronized 關(guān)鍵字之后,這就是一個原子操作了,這是不對的哈。為什么呢?還要從代理類說起,代理類開啟事務(wù),執(zhí)行目標(biāo)方法,提交事務(wù)。而不管是 REQUIRES_NEW 還是 synchronized 關(guān)鍵字都只是作用于目標(biāo)方法,即使目標(biāo)方法執(zhí)行成功,可是事務(wù)還是沒有提交呢。
??對于 insert,delete,update,select — for update,語句來說,都是原子性的。但是 select 不是。
??解決方案就是擴(kuò)大 synchronized 的范圍,為整個代理方法加鎖,而不是把鎖加在目標(biāo)方法上。也可以通過 SQL 來控制,保證操作的原子性,使用 select — for update
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧
網(wǎng)頁名稱:你真的會用Spring事務(wù)@Transactional嗎?-創(chuàng)新互聯(lián)
網(wǎng)站網(wǎng)址:http://www.chinadenli.net/article12/dgecdc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供移動網(wǎng)站建設(shè)、網(wǎng)站導(dǎo)航、網(wǎng)站營銷、標(biāo)簽優(yōu)化、網(wǎng)頁設(shè)計公司、網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)