日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

@Transactional注解的失效场景

發(fā)布時(shí)間:2025/3/12 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 @Transactional注解的失效场景 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

@Transactional注解的失效場(chǎng)景

引言

@Transactional 注解相信大家并不陌生,平時(shí)開(kāi)發(fā)中很常用的一個(gè)注解,它能保證方法內(nèi)多個(gè)數(shù)據(jù)庫(kù)操作要么同時(shí)成功、要么同時(shí)失敗。使用@Transactional注解時(shí)需要注意許多的細(xì)節(jié),不然你會(huì)發(fā)現(xiàn)@Transactional總是莫名其妙的就失效了。

下面我們從what ,where,when四個(gè)方面徹底弄明白如何回答面試官的問(wèn)題。

一、什么是事務(wù)(WHAT)

事務(wù)(Transaction),一般是指要做的或所做的事情。在計(jì)算機(jī)術(shù)語(yǔ)中是指訪問(wèn)并可能更新數(shù)據(jù)庫(kù)中各種數(shù)據(jù)項(xiàng)的一個(gè)程序執(zhí)行單元(unit)。

這里我們以取錢(qián)的例子來(lái)講解:比如你去ATM機(jī)取1000塊錢(qián),大體有兩個(gè)步驟:第一步輸入密碼金額,銀行卡扣掉1000元錢(qián);第二步從ATM出1000元錢(qián)。這兩個(gè)步驟必須是要么都執(zhí)行要么都不執(zhí)行。如果銀行卡扣除了1000塊但是ATM出錢(qián)失敗的話,你將會(huì)損失1000元;如果銀行卡扣錢(qián)失敗但是ATM卻出了1000塊,那么銀行將損失1000元。

如何保證這兩個(gè)步驟不會(huì)出現(xiàn)一個(gè)出現(xiàn)異常了,而另一個(gè)執(zhí)行成功呢?事務(wù)就是用來(lái)解決這樣的問(wèn)題。事務(wù)是一系列的動(dòng)作,它們綜合在一起才是一個(gè)完整的工作單元,這些動(dòng)作必須全部完成,如果有一個(gè)失敗的話,那么事務(wù)就會(huì)回滾到最開(kāi)始的狀態(tài),仿佛什么都沒(méi)發(fā)生過(guò)一樣。在企業(yè)級(jí)應(yīng)用程序開(kāi)發(fā)中,事務(wù)管理是必不可少的技術(shù),用來(lái)確保數(shù)據(jù)的完整性和一致性。

在我們?nèi)粘i_(kāi)發(fā)中事務(wù)分為聲明式事務(wù)和編程式事務(wù)。

編程式事務(wù)

是指在代碼中手動(dòng)的管理事務(wù)的提交、回滾等操作,代碼侵入性比較強(qiáng)。

編程式事務(wù)指的是通過(guò)編碼方式實(shí)現(xiàn)事務(wù),允許用戶在代碼中精確定義事務(wù)的邊界。

即類(lèi)似于JDBC編程實(shí)現(xiàn)事務(wù)管理。管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。

對(duì)于編程式事務(wù)管理,spring推薦使用TransactionTemplate。

try {//TODO somethingtransactionManager.commit(status); } catch (Exception e) {transactionManager.rollback(status);throw new InvoiceApplyException("異常"); }

聲明式事務(wù)

管理建立在AOP之上的。其本質(zhì)是對(duì)方法前后進(jìn)行攔截,然后在目標(biāo)方法開(kāi)始之前創(chuàng)建或者加入一個(gè)事務(wù),在執(zhí)行完目標(biāo)方法之后根據(jù)執(zhí)行情況提交或者回滾事務(wù)。

聲明式事務(wù)最大的優(yōu)點(diǎn)就是不需要通過(guò)編程的方式管理事務(wù),這樣就不需要在業(yè)務(wù)邏輯代碼中摻雜事務(wù)管理的代碼,只需在配置文件中做相關(guān)的事務(wù)規(guī)則聲明(或通過(guò)基于@Transactional注解的方式),便可以將事務(wù)規(guī)則應(yīng)用到業(yè)務(wù)邏輯中。

簡(jiǎn)單地說(shuō),編程式事務(wù)侵入到了業(yè)務(wù)代碼里面,但是提供了更加詳細(xì)的事務(wù)管理;

而聲明式事務(wù)由于基于AOP,所以既能起到事務(wù)管理的作用,又可以不影響業(yè)務(wù)代碼的具體實(shí)現(xiàn)。

聲明式事務(wù)也有兩種實(shí)現(xiàn)方式,一是基于TX和AOP的xml配置文件方式,二種就是基于@Transactional注解了。

@GetMapping("/user") @Transactional public String user() {int insert = userMapper.insert(userInfo); }

二、@Transactional可以在什么地方使用(WHERE)

1、@Transactional注解可以作用于哪些地方?

@Transactional 可以作用在接口、類(lèi)、類(lèi)方法。

  • 作用于類(lèi):當(dāng)把@Transactional 注解放在類(lèi)上時(shí),表示所有該類(lèi)的public方法都配置相同的事務(wù)屬性信息。
  • 作用于方法:當(dāng)類(lèi)配置了@Transactional,方法也配置了@Transactional,方法的事務(wù)會(huì)覆蓋類(lèi)的事務(wù)配置信息。
  • 作用于接口:不推薦這種使用方法,因?yàn)橐坏?biāo)注在Interface上并且配置了Spring AOP 使用CGLib動(dòng)態(tài)代理,將會(huì)導(dǎo)致@Transactional注解失效
@Transactional @RestController @RequestMapping public class MybatisPlusController {@Autowiredprivate UserMapper userMapper;@Transactional(rollbackFor = Exception.class)@GetMapping("/user")public String test() throws Exception {User user = new User();user.setName("javaHuang");user.setAge("2");user.setSex("2");int insert = userMapper.insert(cityInfoDict);return insert + "";} }

2、@Transactional屬性詳解

propagation屬性

propagation 代表事務(wù)的傳播行為,默認(rèn)值為 Propagation.REQUIRED,其他的屬性信息如下:

  • Propagation.REQUIRED:如果當(dāng)前存在事務(wù),則加入該事務(wù),如果當(dāng)前不存在事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。( 也就是說(shuō)如果A方法和B方法都添加了注解,在默認(rèn)傳播模式下,A方法內(nèi)部調(diào)用B方法,會(huì)把兩個(gè)方法的事務(wù)合并為一個(gè)事務(wù)
  • Propagation.SUPPORTS:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前不存在事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。
  • Propagation.MANDATORY:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前不存在事務(wù),則拋出異常。
  • Propagation.REQUIRES_NEW:重新創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),暫停當(dāng)前的事務(wù)。( 當(dāng)類(lèi)A中的 a 方法用默認(rèn)Propagation.REQUIRED模式,類(lèi)B中的 b方法加上采用 Propagation.REQUIRES_NEW模式,然后在 a 方法中調(diào)用 b方法操作數(shù)據(jù)庫(kù),然而 a方法拋出異常后,b方法并沒(méi)有進(jìn)行回滾,因?yàn)镻ropagation.REQUIRES_NEW會(huì)暫停 a方法的事務(wù) )
  • Propagation.NOT_SUPPORTED:以非事務(wù)的方式運(yùn)行,如果當(dāng)前存在事務(wù),暫停當(dāng)前的事務(wù)。
  • Propagation.NEVER:以非事務(wù)的方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常。
  • Propagation.NESTED :和 Propagation.REQUIRED 效果一樣。

isolation 屬性

isolation :事務(wù)的隔離級(jí)別,默認(rèn)值為 Isolation.DEFAULT。

TransactionDefinition.ISOLATION_DEFAULT

這是默認(rèn)值,表示使用底層數(shù)據(jù)庫(kù)的默認(rèn)隔離級(jí)別。對(duì)大部分?jǐn)?shù)據(jù)庫(kù)而言,通常這值就是

TransactionDefinition.ISOLATION_READ_UNCOMMITTED

該隔離級(jí)別表示一個(gè)事務(wù)可以讀取另一個(gè)事務(wù)修改但還沒(méi)有提交的數(shù)據(jù)。該級(jí)別不能防止臟讀,不可重復(fù)讀和幻讀,因此很少使用該隔離級(jí)別。比如PostgreSQL實(shí)際上并沒(méi)有此級(jí)別。

TransactionDefinition.ISOLATION_READ_COMMITTED

該隔離級(jí)別表示一個(gè)事務(wù)只能讀取另一個(gè)事務(wù)已經(jīng)提交的數(shù)據(jù)。該級(jí)別可以防止臟讀,這也是大多數(shù)情況下的推薦值。

TransactionDefinition.ISOLATION_REPEATABLE_READ

該隔離級(jí)別表示一個(gè)事務(wù)在整個(gè)過(guò)程中可以多次重復(fù)執(zhí)行某個(gè)查詢,并且每次返回的記錄都相同。該級(jí)別可以防止臟讀和不可重復(fù)讀。

TransactionDefinition.ISOLATION_SERIALIZABLE

所有的事務(wù)依次逐個(gè)執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說(shuō),該級(jí)別可以防止臟讀、不可重復(fù)讀以及幻讀。但是這將嚴(yán)重影響程序的性能。通常情況下也不會(huì)用到該級(jí)別。

timeout 屬性

timeout :事務(wù)的超時(shí)時(shí)間,默認(rèn)值為 -1。如果超過(guò)該時(shí)間限制但事務(wù)還沒(méi)有完成,則自動(dòng)回滾事務(wù)。

readOnly 屬性

readOnly :指定事務(wù)是否為只讀事務(wù),默認(rèn)值為 false;為了忽略那些不需要事務(wù)的方法,比如讀取數(shù)據(jù),可以設(shè)置 read-only 為 true。

rollbackFor 屬性

rollbackFor :用于指定能夠觸發(fā)事務(wù)回滾的異常類(lèi)型,可以指定多個(gè)異常類(lèi)型。

noRollbackFor屬性**

noRollbackFor:拋出指定的異常類(lèi)型,不回滾事務(wù),也可以指定多個(gè)異常類(lèi)型。

二、@Transactional什么時(shí)候會(huì)失效(WHEN)

面試官就直接問(wèn)我有沒(méi)有用過(guò)@Transactional,我肯定不能說(shuō)沒(méi)用過(guò)啊,十分自信的說(shuō),常用。

面試官又問(wèn)我,在實(shí)際開(kāi)發(fā)過(guò)程有沒(méi)有遇到過(guò)@Transactional失效的情況,我肯定不能說(shuō)沒(méi)有啊,再次十分自信的說(shuō)到,經(jīng)常。

面試官一臉問(wèn)號(hào),經(jīng)常???那你給我說(shuō)說(shuō)@Transactional在什么時(shí)候會(huì)失效呢?

下面的內(nèi)容是我將我面試時(shí)說(shuō)的失效場(chǎng)景整理了一下。

1、@Transactional 應(yīng)用在非 public 修飾的方法上

如果Transactional注解應(yīng)用在非public 修飾的方法上,Transactional將會(huì)失效。

之所以會(huì)失效是因?yàn)樵赟pring AOP 代理時(shí),TransactionInterceptor(事務(wù)攔截器)在目標(biāo)方法執(zhí)行前后進(jìn)行攔截,DynamicAdvisedInterceptor(CglibAopProxy 的內(nèi)部類(lèi))的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法會(huì)間接調(diào)用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute`方法,獲取Transactional 注解的事務(wù)配置信息。

protected TransactionAttribute computeTransactionAttribute(Methodmethod,Class<?> targetClass) {// Don't allow no-public methods as required.if (allowPublicMethodsOnly() &&!Modifier.isPublic(method.getModifiers())) {return null; }

Modifier.isPublic會(huì)檢查目標(biāo)方法的修飾符是否為 public,不是 public則不會(huì)獲取@Transactional 的屬性配置信息。

注意:protected、private 修飾的方法上使用 @Transactional 注解,雖然事務(wù)無(wú)效,但不會(huì)有任何報(bào)錯(cuò),這是我們很容犯錯(cuò)的一點(diǎn)。

2、數(shù)據(jù)庫(kù)引擎要不支持事務(wù)

數(shù)據(jù)庫(kù)引擎要支持事務(wù),如果是MySQL,注意表要使用支持事務(wù)的引擎,比如innodb,如果是myisam,事務(wù)是不起作用的。

3、@由于propagation 設(shè)置錯(cuò)誤,導(dǎo)致注解失效

在上面解讀propagation 屬性的時(shí)候,我們知道

TransactionDefinition.PROPAGATION_SUPPORTS

如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。

TransactionDefinition.PROPAGATION_NOT_SUPPORTED

以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。

TransactionDefinition.PROPAGATION_NEVER

以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常。

當(dāng)我們將propagation 屬性設(shè)置為上述三種時(shí),@Transactional 注解就不會(huì)產(chǎn)生效果

4、rollbackFor 設(shè)置錯(cuò)誤,@Transactional 注解失效

上述我們解讀rollbackFor 屬性的時(shí)候我們知道

rollbackFor 可以指定能夠觸發(fā)事務(wù)回滾的異常類(lèi)型。

Spring默認(rèn)拋出了未檢查unchecked異常(繼承自 RuntimeException 的異常)或者 Error才回滾事務(wù);

其他異常不會(huì)觸發(fā)回滾事務(wù)。如果在事務(wù)中拋出其他類(lèi)型的異常,但卻期望 Spring 能夠回滾事務(wù),就需要指定 rollbackFor屬性。

// 希望自定義的異常可以進(jìn)行回滾 @Transactional(propagation= Propagation.REQUIRED,rollbackFor=MyException.class

若在目標(biāo)方法中拋出的異常是 rollbackFor 指定的異常的子類(lèi),事務(wù)同樣會(huì)回滾。Spring源碼如下:

private int getDepth(Class<?> exceptionClass, int depth) {if (exceptionClass.getName().contains(this.exceptionName)) {// Found it!return depth; }// If we've gone as far as we can go and haven't found it...if (exceptionClass == Throwable.class) {return -1; } return getDepth(exceptionClass.getSuperclass(), depth + 1); }

5、方法之間的互相調(diào)用也會(huì)導(dǎo)致@Transactional失效

我們來(lái)看下面的場(chǎng)景:

比如有一個(gè)類(lèi)User,它的一個(gè)方法A,A再調(diào)用本類(lèi)的方法B(不論方法B是用public還是private修飾),但方法A沒(méi)有聲明注解事務(wù),而B(niǎo)方法有。則外部調(diào)用方法A之后,方法B的事務(wù)是不會(huì)起作用的。這也是經(jīng)常犯錯(cuò)誤的一個(gè)地方。

那為啥會(huì)出現(xiàn)這種情況?其實(shí)這還是由于使用Spring AOP代理造成的,因?yàn)橹挥挟?dāng)事務(wù)方法被當(dāng)前類(lèi)以外的代碼調(diào)用時(shí),才會(huì)由Spring生成的代理對(duì)象來(lái)管理。

//@Transactional@GetMapping("/user")private Integer A() throws Exception {User user = new User();user.setName("javaHuang");/*** B 插入字段為 topJavaer的數(shù)據(jù)*/this.insertB();/*** A 插入字段為 2的數(shù)據(jù)*/int insert = userMapper.insert(user);return insert;}@Transactional()public Integer insertB() throws Exception {User user = new User();user.setName("topJavaer");return userMapper.insert(user);}

6、異常被你的 catch“吃了”導(dǎo)致@Transactional失效

這種情況是最常見(jiàn)的一種@Transactional注解失效場(chǎng)景,

@Transactionalprivate Integer A() throws Exception {int insert = 0;try {User user = new User();user.setCityName("javaHuang");user.setUserId(1);/*** A 插入字段為 javaHuang的數(shù)據(jù)*/insert = userMapper.insert(user);/*** B 插入字段為 topJavaer的數(shù)據(jù)*/b.insertB();} catch (Exception e) {e.printStackTrace();}}

如果B方法內(nèi)部拋了異常,而A方法此時(shí)try catch了B方法的異常,那這個(gè)事務(wù)就不能正常回滾,而是會(huì)報(bào)出異常

org.springframework.transaction.UnexpectedRollbackException: Transactionrolled back because it has been marked as rollback-only

解決方法:

第一:聲明事務(wù)的時(shí)候加上rollback=‘exception’

第二 :cath代碼塊里面手動(dòng)回滾

總結(jié)

@Transactional 注解我們經(jīng)常使用,但是往往我們也只是知道它是一個(gè)事務(wù)注解,很多時(shí)候遇到事務(wù)注解失效的情況下,我們都是一頭霧水,看不出個(gè)所以然來(lái),花費(fèi)了很長(zhǎng)的時(shí)間都不能解決。

通過(guò)本文了解了@Transactional 注解的失效場(chǎng)景,在以后遇到這種情況時(shí),基本就能一眼看破,然后摸摸自己光滑的腦門(mén),soga,so easy!

媽媽再也不用擔(dān)心我找不到自己寫(xiě)的bug了。




分析spring事務(wù)@Transactional注解在同一個(gè)類(lèi)中的方法之間調(diào)用不生效的原因及解決方案

問(wèn)題:

在Spring管理的項(xiàng)目中,方法A使用了Transactional注解,試圖實(shí)現(xiàn)事務(wù)性。但當(dāng)同一個(gè)class中的方法B調(diào)用方法A時(shí),會(huì)發(fā)現(xiàn)方法A中的異常不再導(dǎo)致回滾,也即事務(wù)失效了。

當(dāng)這個(gè)方法被同一個(gè)類(lèi)調(diào)用的時(shí)候,spring無(wú)法將這個(gè)方法加到事務(wù)管理中。

我們來(lái)看一下生效時(shí)候和不生效時(shí)候調(diào)用堆棧日志的對(duì)比。

通過(guò)對(duì)比兩個(gè)調(diào)用堆棧可以看出,spring的@Transactional事務(wù)生效的一個(gè)前提是進(jìn)行方法調(diào)用前經(jīng)過(guò)攔截器TransactionInterceptor,也就是說(shuō)只有通過(guò)TransactionInterceptor攔截器的方法才會(huì)被加入到spring事務(wù)管理中,查看spring源碼可以看到,在AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice方法中會(huì)從調(diào)用方法中獲取@Transactional注解,如果有該注解,則啟用事務(wù),否則不啟用。

這個(gè)方法是通過(guò)spring的AOP類(lèi)CglibAopProxy的內(nèi)部類(lèi)DynamicAdvisedInterceptor調(diào)用的,而DynamicAdvisedInterceptor繼承了MethodInterceptor,用于攔截方法調(diào)用,并從中獲取調(diào)用鏈。

如果是在同一個(gè)類(lèi)中的方法調(diào)用,則不會(huì)被方法攔截器攔截到,因此事務(wù)不會(huì)起作用,必須將方法放入另一個(gè)類(lèi),并且該類(lèi)通過(guò)spring注入。

原因:

Transactional是Spring提供的事務(wù)管理注解。

重點(diǎn)在于,Spring采用動(dòng)態(tài)代理(AOP)實(shí)現(xiàn)對(duì)bean的管理和切片,它為我們的每個(gè)class生成一個(gè)代理對(duì)象。只有在代理對(duì)象之間進(jìn)行調(diào)用時(shí),可以觸發(fā)切面邏輯。

而在同一個(gè)class中,方法B調(diào)用方法A,調(diào)用的是原對(duì)象的方法,而不通過(guò)代理對(duì)象。所以Spring無(wú)法切到這次調(diào)用,也就無(wú)法通過(guò)注解保證事務(wù)性了。

也就是說(shuō),在同一個(gè)類(lèi)中的方法調(diào)用,則不會(huì)被方法攔截器攔截到,因此事務(wù)不會(huì)起作用。

解決方法1:

將事務(wù)方法放到另一個(gè)類(lèi)中(或者單獨(dú)開(kāi)啟一層,取名“事務(wù)層”)進(jìn)行調(diào)用,即符合了在對(duì)象之間調(diào)用的條件。

解決方法2:

獲取本對(duì)象的代理對(duì)象,再進(jìn)行調(diào)用。具體操作如:

  • Spring-content.xml上下文中,增加配置:<aop:aspectj-autoproxy expose-proxy=“true”/>

  • 在xxxServiceImpl中,用(xxxService)(AopContext.currentProxy()),獲取到xxxService的代理類(lèi),再調(diào)用事務(wù)方法,強(qiáng)行經(jīng)過(guò)代理類(lèi),激活事務(wù)切面。

  • 解決方法3:

    很多時(shí)候,方法內(nèi)調(diào)用又希望激活事務(wù),是由于同一個(gè)方法既有DAO操作又有I/O等耗時(shí)操作,不想讓耗時(shí)的I/O造成事務(wù)的太長(zhǎng)耗時(shí)(比如新增商品同時(shí)需要寫(xiě)入庫(kù)存)。此時(shí),可以將I/O做成異步操作(如加入線程池),而加入線程池的操作即便加入事務(wù)也不會(huì)導(dǎo)致事務(wù)太長(zhǎng),問(wèn)題可以迎刃而解。

    解決方法4:

    用@Autowired 注入自己 然后在用注入的bean調(diào)用自己的方法也可以

    參考:
    https://blog.csdn.net/ligeforrent/article/details/79996797

    https://www.jianshu.com/p/2e4e1007edf2

    總結(jié)

    以上是生活随笔為你收集整理的@Transactional注解的失效场景的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。