精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Spring事務在哪幾種情況下會不生效?

jf_ro2CN3Fa ? 來源:撿田螺的小男孩 ? 2023-05-10 17:53 ? 次閱讀

前言

日常開發中,我們經常使用到spring事務。最近一位朋友去美團面試,被問了這么一道面試題: Spring 事務在哪幾種情況下會不生效? 今天田螺哥跟大家聊聊,spring事務不生效 的15種場景。

1. 你的service類沒有被Spring管理

//@Service(注釋了@Service)
publicclassTianLuoServiceImplimplementsTianLuoService{

@Autowired
privateTianLuoMappertianLuoMapper;

@Autowired
privateTianLuoFlowMappertianLuoFlowMapper;

@Transactional
publicvoidaddTianLuo(TianLuotianluo){
//保存tianluo實體數據庫記錄
tianLuoMapper.save(tianluo);
//保存tianluo流水數據庫記錄
tianLuoFlowMapper.saveFlow(buildFlowByTianLuo(tianluo));
}
}

事務不生效的原因 :上面例子中, @Service注解注釋之后,spring事務(@Transactional)沒有生效,因為Spring事務是由AOP機制實現的,也就是說從Spring IOC容器獲取bean時,Spring會為目標類創建代理,來支持事務的。但是@Service被注釋后,你的service類都不是spring管理的,那怎么創建代理類來支持事務呢

解決方案 :加上@Service注解。

2.沒有在Spring配置文件中啟用事務管理器

@Configuration
publicclassAppConfig{
//沒有配置事務管理器
}

@Service
publicclassMyService{
@Transactional
publicvoiddoSomething(){
//...
}
}

事務不生效的原因 :沒有在AppConfig中配置事務管理器,因此Spring無法創建事務代理對象,導致事務不生效。即使在MyService中添加了@Transactional注解,該方法也不會被Spring管理的事務代理攔截。

解決方案 :為了解決這個問題,應該在AppConfig中配置一個事務管器。例如:

@Configuration
publicclassAppConfig{
@Bean
publicPlatformTransactionManagertransactionManager(){
returnnewDataSourceTransactionManager(dataSource());
}
}

@Service
publicclassMyService{
@Transactional
publicvoiddoSomething(){
//...
}
}

如果是Spring Boot項目,它默認會自動配置事務管理器并開啟事務支持。

3. 事務方法被final、static關鍵字修飾

@Service
publicclassTianLuoServiceImpl{

@Autowired
privateTianLuoMappertianLuoMapper;

@Autowired
privateTianLuoFlowMappertianLuoFlowMapper;

@Transactional
publicfinalvoidaddTianLuo(TianLuotianluo){
//保存tianluo實體數據庫記錄
tianLuoMapper.save(tianluo);
//保存tianluo流水數據庫記錄
tianLuoFlowMapper.saveFlow(buildFlowByTianLuo(tianluo));
}
}

事務不生效的原因 :如果一個方法被聲明為final或者static,則該方法不能被子類重寫,也就是說無法在該方法上進行動態代理,這會導致Spring無法生成事務代理對象來管理事務。

解決方案 :addTianLuo事務方法 不要用final修飾或者static修飾。

4. 同一個類中,方法內部調用

@Service
publicclassTianLuoServiceImplimplementsTianLuoService{

@Autowired
privateTianLuoMappertianLuoMapper;

@Autowired
privateTianLuoFlowMappertianLuoFlowMapper;

publicvoidaddTianLuo(TianLuotianluo){
//調用內部的事務方法
this.executeAddTianLuo(tianluo);
}

@Transactional
publicvoidexecuteAddTianLuo(TianLuotianluo){
tianLuoMapper.save(tianluo);
tianLuoFlowMapper.saveFlow(buildFlowByTianLuo(tianluo));
}
}

事務不生效的原因 : 事務是通過Spring AOP代理來實現的,而在同一個類中,一個方法調用另一個方法時,調用方法直接調用目標方法的代碼,而不是通過代理類進行調用 。即以上代碼,調用目標executeAddTianLuo方法不是通過代理類進行的,因此事務不生效。

解決方案 :可以新建多一個類,讓這兩個方法分開,分別在不同的類中。如下:

@Service
publicclassTianLuoExecuteServiceImplimplementsTianLuoExecuteService{

@Autowired
privateTianLuoMappertianLuoMapper;
@Autowired
privateTianLuoFlowMappertianLuoFlowMapper;

@Transactional
publicvoidexecuteAddTianLuo(TianLuotianluo){
tianLuoMapper.save(tianluo);
tianLuoFlowMapper.saveFlow(buildFlowByTianLuo(tianluo));
}
}

@Service
publicclassTianLuoAddServiceImplimplementsTianLuoAddService{

@Autowired
privateTianLuoExecuteServicetianLuoExecuteService;

publicvoidaddTianLuo(Useruser){
tianLuoExecuteService.executeAddTianLuo(user);
}
}

當然,有時候你也可以在該 Service 類中注入自己,或者通過AopContext.currentProxy()獲取代理對象。

5.方法的訪問權限不是public

@Service
publicclassTianLuoServiceImplimplementsTianLuoService{

@Autowired
privateTianLuoMappertianLuoMapper;

@Autowired
privateTianLuoFlowMappertianLuoFlowMapper;

@Transactional
privatevoidaddTianLuo(TianLuotianluo){
tianLuoMapper.save(tianluo);
tianLuoFlowMapper.saveFlow(buildFlowByTianLuo(tianluo));
}
}

事務不生效的原因 :spring事務方法addTianLuo的訪問權限不是public,所以事務就不生效啦,因為Spring事務是由AOP機制實現的,AOP機制的本質就是動態代理,而代理的事務方法不是public的話,computeTransactionAttribute()就會返回null,也就是這時事務屬性不存在了。大家可以看下AbstractFallbackTransactionAttributeSource的源碼:

86e87cbe-e41d-11ed-ab56-dac502259ad0.png

解決方案 :addTianLuo事務方法的訪問權限修改為public。

6. 數據庫的存儲引擎不支持事務

Spring事務的底層,還是依賴于數據庫本身的事務支持。在MySQL中,MyISAM存儲引擎是不支持事務的,InnoDB引擎才支持事務。因此開發階段設計表的時候,確認你的選擇的存儲引擎是支持事務的

870c83f2-e41d-11ed-ab56-dac502259ad0.png

7 .配置錯誤的 @Transactional 注解

@Transactional(readOnly=true)
publicvoidupdateUser(Useruser){
userDao.updateUser(user);
}

事務不生效的原因 :雖然使用了@Transactional注解,但是注解中的readOnly=true屬性指示這是一個只讀事務,因此在更新User實體時會拋出異常。

解決方案 :將readOnly屬性設置為false,或者移除了@Transactional注解中的readOnly屬性。

8.事務超時時間設置過短

@Transactional(timeout=1)
publicvoiddoSomething(){
//...
}

事務不生效的原因 :在上面的例子中,timeout屬性被設置為1秒,這意味著如果事務在1 秒內無法完成,則報事務超時了。

9. 使用了錯誤的事務傳播機制

@Service
publicclassTianLuoServiceImpl{

@Autowired
privateTianLuoMappertianLuoMapper;
@Autowired
privateTianLuoFlowMappertianLuoFlowMapper;

@Transactional(propagation=Propagation.NOT_SUPPORTED)
publicvoiddoInsertTianluo(TianLuotianluo)throwsException{
tianLuoMapper.save(tianluo);
tianLuoFlowMapper.saveFlow(buildFlowByTianLuo(tianluo));
}
}

事務不生效的原因 :Propagation.NOT_SUPPORTED傳播特性不支持事務。

解決方案 :選擇正確的事務傳播機制。

幫大家復習一下,Spring提供了七種事務傳播機制。它們分別是:

REQUIRED(默認):如果當前存在一個事務,則加入該事務;否則,創建一個新事務。該傳播級別表示方法必須在事務中執行。

SUPPORTS:如果當前存在一個事務,則加入該事務;否則,以非事務的方式繼續執行。

MANDATORY:如果當前存在一個事務,則加入該事務;否則,拋出異常。

REQUIRES_NEW:創建一個新的事務,并且如果存在一個事務,則將該事務掛起。

NOT_SUPPORTED:以非事務方式執行操作,如果當前存在一個事務,則將該事務掛起。

NEVER:以非事務方式執行操作,如果當前存在一個事務,則拋出異常。

NESTED:如果當前存在一個事務,則在嵌套事務內執行。如果沒有事務,則按REQUIRED傳播級別執行。嵌套事務是外部事務的一部分,可以在外部事務提交或回滾時部分提交或回滾。

10. rollbackFor屬性配置錯誤

@Service
publicclassTianLuoServiceImplimplementsTianLuoService{

@Autowired
privateTianLuoMappertianLuoMapper;

@Autowired
privateTianLuoFlowMappertianLuoFlowMapper;

@Transactional(rollbackFor=Error.class)
publicvoidaddTianLuo(TianLuotianluo){
//保存tianluo數據庫記錄
tianLuoMapper.save(tianluo);
//保存tianluo流水數據庫記錄
tianLuoFlowMapper.saveFlow(tianluo);
//模擬異常拋出
thrownewException();
}
}

事務不生效的原因 : 其實rollbackFor屬性指定的異常必須是Throwable或者其子類。默認情況下,RuntimeException和Error兩種異常都是會自動回滾的。但是因為以上的代碼例子,指定了rollbackFor = Error.class,但是拋出的異常又是Exception,而Exception和Error沒有任何什么繼承關系,因此事務就不生效。

871cf43a-e41d-11ed-ab56-dac502259ad0.png

大家可以看一下Transactional注解源碼哈:

872f0f30-e41d-11ed-ab56-dac502259ad0.png

解決方案 :rollbackFor屬性指定的異常與拋出的異常匹配。

11.事務注解被覆蓋導致事務失效

publicinterfaceMyRepository{
@Transactional
voidsave(Stringdata);
}

publicclassMyRepositoryImplimplementsMyRepository{
@Override
publicvoidsave(Stringdata){
//數據庫操作
}
}

publicclassMyService{

@Autowired
privateMyRepositorymyRepository;

@Transactional
publicvoiddoSomething(Stringdata){
myRepository.save(data);
}
}

publicclassMyTianluoServiceextendsMyService{
@Transactional(propagation=Propagation.REQUIRES_NEW)
publicvoiddoSomething(Stringdata){
super.doSomething(data);
}
}

事務失效的原因 :MyTianluoService是MyService的子類,并且覆蓋了doSomething()方法。在該方法中,使用了不同的傳播行為(REQUIRES_NEW)來覆蓋父類的@Transactional注解。在這種情況下,當調用MyTianluoService的doSomething()方法時,由于子類方法中的注解覆蓋了父類的注解,Spring框架將不會在父類的方法中啟動事務 。因此,當MyRepository的save()方法被調用時,事務將不會被啟動,也不會回滾。這將導致數據不一致的問題,因為在MyRepository的save()方法中進行的數據庫操作將不會回滾。

12.嵌套事務的坑

@Service
publicclassTianLuoServiceInOutService{

@Autowired
privateTianLuoFlowServicetianLuoFlowService;
@Autowired
privateTianLuoMappertianLuoMapper;

@Transactional
publicvoidaddTianLuo(TianLuotianluo)throwsException{
tianLuoMapper.save(tianluo);
tianLuoFlowService.saveFlow(tianluo);
}
}

@Service
publicclassTianLuoFlowService{

@Autowired
privateTianLuoFlowMappertianLuoFlowMapper;

@Transactional(propagation=Propagation.NESTED)
publicvoidsaveFlow(TianLuotianLuo){
tianLuoFlowMapper.save(tianLuo);
thrownewRuntimeException();
}
}

以上代碼使用了嵌套事務,如果saveFlow出現運行時異常,會繼續往上拋,到外層addTianLuo的方法,導致tianLuoMapper.save也會回滾啦。如果不想因為被內部嵌套的事務影響 ,可以用try-catch包住,如下:

@Transactional
publicvoidaddTianLuo(TianLuotianluo)throwsException{
tianLuoMapper.save(tianluo);
try{
tianLuoFlowService.saveFlow(tianluo);
}catch(Exceptione){
log.error("savetianluoflowfail,message:{}",e.getMessage());
}
}

13. 事務多線程調用

@Service
publicclassTianLuoService{

@Autowired
privateTianLuoMappertianLuoMapper;

@Autowired
privateTianLuoFlowServicetianLuoFlowService;

@Transactional
publicvoidaddTianLuo(TianLuotianluo){
//保存tianluo數據庫記錄
tianLuoMapper.save(tianluo);
//多線程調用
newThread(()->{
tianLuoFlowService.saveFlow(tianluo);
}).start();
}
}

@Service
publicclassTianLuoFlowService{

@Autowired
privateTianLuoFlowMappertianLuoFlowMapper;

@Transactional
publicvoidsave(TianLuotianLuo){
tianLuoFlowMapper.saveFlow(tianLuo);
}
}

事務不生效原因 :這是因為Spring事務是基于線程綁定的,每個線程都有自己的事務上下文 ,而多線程環境下可能會存在多個線程共享同一個事務上下文的情況,導致事務不生效。Spring事務管理器通過使用線程本地變量(ThreadLocal)來實現線程安全。大家有興趣的話,可以去看下源碼哈.

在Spring事務管理器中,通過TransactionSynchronizationManager類來管理事務上下文。TransactionSynchronizationManager內部維護了一個ThreadLocal對象,用來存儲當前線程的事務上下文。在事務開始時,TransactionSynchronizationManager會將事務上下文綁定到當前線程的ThreadLocal對象中,當事務結束時,TransactionSynchronizationManager會將事務上下文從ThreadLocal對象中移除。

8779cb2e-e41d-11ed-ab56-dac502259ad0.png

14.異常被捕獲并處理了,沒有重新拋出

@Service
publicclassTianLuoServiceImplimplementsTianLuoService{

@Autowired
privateTianLuoMappertianLuoMapper;

@Autowired
privateTianLuoFlowMappertianLuoFlowMapper;

@Transactional
publicvoidaddTianLuo(TianLuotianluo){
try{
//保存tianluo數據庫記錄
tianLuoMapper.save(tianluo);
//保存tianluoflow數據庫記錄
tianLuoFlowMapper.saveFlow(tianluo);
}catch(Exceptione){
log.error("addTianLuoerror,id:{},message:{}",tianluo.getId(),e.getMessage());
}
}

}

事務不生效的原因 : 事務中的異常已經被業務代碼捕獲并處理,而沒有被正確地傳播回事務管理器,事務將無法回滾。我們可以從spring源碼(TransactionAspectSupport這個類)中找到答案:

publicabstractclassTransactionAspectSupportimplementsBeanFactoryAware,InitializingBean{

//這方法會省略部分代碼,只留關鍵代碼哈
@Nullable
protectedObjectinvokeWithinTransaction(Methodmethod,@NullableClasstargetClass,finalInvocationCallbackinvocation)throwsThrowable{

if(txAttr==null||!(ptminstanceofCallbackPreferringPlatformTransactionManager)){

TransactionInfotxInfo=createTransactionIfNecessary(ptm,txAttr,joinpointIdentification);
ObjectretVal;
try{
//SpringAOP中MethodInterceptor接口的一個方法,它允許攔截器在執行被代理方法之前和之后執行額外的邏輯。
retVal=invocation.proceedWithInvocation();
}
catch(Throwableex){
//用于在發生異常時完成事務(如果Springcatch不到對應的異常的話,就不會進入回滾事務的邏輯)
completeTransactionAfterThrowing(txInfo,ex);
throwex;
}
finally{
cleanupTransactionInfo(txInfo);
}

//用于在方法正常返回后提交事務。
commitTransactionAfterReturning(txInfo);
returnretVal;
}
}

在invokeWithinTransaction方法中,當Spring catch到Throwable異常的時候,就會調用completeTransactionAfterThrowing()方法進行事務回滾的邏輯。但是,在TianLuoServiceImpl類的spring事務方法addTianLuo中,直接把異常catch住了,并沒有重新throw出來,因此 Spring自然就catch不到異常啦,因此事務回滾的邏輯就不會執行,事務就失效了。

解決方案 :在spring事務方法中,當我們使用了try-catch,如果catch住異常,記錄完異常日志什么的,一定要重新把異常拋出來,正例如下:

@Service
publicclassTianLuoServiceImplimplementsTianLuoService{

@Autowired
privateTianLuoMappertianLuoMapper;

@Autowired
privateTianLuoFlowMappertianLuoFlowMapper;

@Transactional(rollbackFor=Exception.class)
publicvoidaddTianLuo(TianLuotianluo){
try{
//保存tianluo數據庫記錄
tianLuoMapper.save(tianluo);
//保存tianluoflow數據庫記錄
tianLuoFlowMapper.saveFlow(tianluo);
}catch(Exceptione){
log.error("addTianLuoerror,id:{},message:{}",tianluo.getId(),e.getMessage());
throwe;
}
}
}

15. 手動拋了別的異常

@Service
publicclassTianLuoServiceImplimplementsTianLuoService{

@Autowired
privateTianLuoMappertianLuoMapper;

@Autowired
privateTianLuoFlowMappertianLuoFlowMapper;

@Transactional
publicvoidaddTianLuo(TianLuotianluo)throwsException{
//保存tianluo數據庫記錄
tianLuoMapper.save(tianluo);
//保存tianluo流水數據庫記錄
tianLuoFlowMapper.saveFlow(tianluo);
thrownewException();
}
}

失效的原因 :上面的代碼例子中,手動拋了Exception異常,但是是不會回滾的,因為Spring默認只處理RuntimeException和Error,對于普通的Exception不會回滾,除非,用rollbackFor屬性指定配置。

解決方案:添加屬性配置@Transactional(rollbackFor = Exception.class)。

注解為事務范圍的方法中,事務的回滾僅僅對于unchecked的異常有效。對于checked異常無效。也就是說事務回滾僅僅發生在,出現RuntimeException或Error的時候。通俗一點就是:代碼中出現的空指針等異常,會被回滾。而文件讀寫、網絡超時問題等,spring就沒法回滾了。




審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • MySQL
    +關注

    關注

    1

    文章

    802

    瀏覽量

    26452
  • AOP
    AOP
    +關注

    關注

    0

    文章

    40

    瀏覽量

    11090
  • MYSQL數據庫
    +關注

    關注

    0

    文章

    95

    瀏覽量

    9383

原文標題:美團二面:spring事務不生效的15種場景

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    EMI濾波器有哪幾種應用和選擇?

    EMI濾波器有哪幾種應用和選擇?|深圳比創達EMC
    的頭像 發表于 10-09 10:32 ?1391次閱讀
    EMI濾波器有<b class='flag-5'>哪幾種</b>應用和選擇?

    JLink下載不了的情況哪幾種

    JLink為什么會下載不了呢?JLink下載不了的情況哪幾種
    發表于 10-21 09:25

    直流屏充電模塊什么樣的情況會被燒壞呢?有哪幾種情況

    直流屏充電模塊什么樣的情況下會被燒壞呢?有哪幾種情況?麻煩大家給分析分析,謝謝
    發表于 07-24 10:54

    在哪幾種情況下造成伺服電機抖動,怎樣解決

    在哪幾種情況下造成伺服電機抖動?怎樣才能解決這些伺服電機抖動帶來的問題?分別是怎么解決的? 例如:加減速時間設置得過小,伺服電機在突然的啟動或者停止的時候產生高慣性抖動......
    的頭像 發表于 11-05 10:40 ?8571次閱讀

    SMT元器件有哪幾種類型

    本文檔的主要內容詳細介紹的是SMT元器件有哪幾種類型。
    發表于 11-27 08:00 ?3次下載

    在哪幾種情況下造成伺服電機抖動

    在哪幾種情況下造成伺服電機抖動?怎樣才能解決這些伺服電機抖動帶來的問題?分別是怎么解決的?
    的頭像 發表于 02-22 16:14 ?1976次閱讀

    造成伺服電機抖動的原因是什么?

    在哪幾種情況下造成伺服電機抖動?怎樣才能解決這些伺服電機抖動帶來的問題?分別是怎么解決的?
    發表于 03-29 06:05 ?26次下載
    造成伺服電機抖動的原因是什么?

    風機軸維修有哪幾種工藝

    風機軸維修有哪幾種工藝
    發表于 12-03 17:47 ?9次下載

    軸承跑內圓有哪幾種修復方式

    軸承跑內圓有哪幾種修復方式
    發表于 01-23 11:07 ?7次下載

    軸承孔磨損維修有哪幾種方法?

    軸承孔磨損維修有哪幾種方法?
    發表于 04-01 16:30 ?11次下載

    什么情況造成伺服電機抖動?

    在哪幾種情況下造成伺服電機抖動?怎樣才能解決這些伺服電機抖動帶來的問題?分別是怎么解決的?
    發表于 11-14 11:53 ?978次閱讀

    造成伺服電機抖動的原因有哪些

    在哪幾種情況下造成伺服電機抖動?怎樣才能解決這些伺服電機抖動帶來的問題?分別是怎么解決的?
    的頭像 發表于 01-04 09:58 ?2938次閱讀

    ESD模型有哪幾種你知道嗎?

    ESD模型有哪幾種你知道嗎?
    的頭像 發表于 05-09 10:00 ?1824次閱讀
    ESD模型有<b class='flag-5'>哪幾種</b>你知道嗎?

    8個Spring事務失效的場景介紹

    作為Java開發工程師,相信大家對Spring事務的使用并不陌生。但是你可能只是停留在基礎的使用層面上,在遇到一些比較特殊的場景,事務可能沒有生效,直接在生產上暴露了,這可能就會導致
    的頭像 發表于 05-11 10:41 ?617次閱讀
    8個<b class='flag-5'>Spring</b><b class='flag-5'>事務</b>失效的場景介紹

    在哪幾種情況下造成伺服電機抖動?

    在哪幾種情況下造成伺服電機抖動?怎樣才能解決這些伺服電機抖動帶來的問題?分別是怎么解決的?
    發表于 05-24 09:41 ?369次閱讀