溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring中@Transactional事務(wù)不生效如何解決

發(fā)布時間:2021-06-22 16:00:52 來源:億速云 閱讀:978 作者:Leah 欄目:編程語言

這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)Spring中@Transactional事務(wù)不生效如何解決,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

一、Spring事務(wù)管理方式

事務(wù)管理在系統(tǒng)開發(fā)中是不可缺少的一部分,Spring提供了很好事務(wù)管理機(jī)制,主要分為編程式事務(wù)聲明式事務(wù)兩種。

  1. 編碼式事務(wù)管理:將事務(wù)控制代碼編寫在業(yè)務(wù)代碼之中。

  2. 聲明式事務(wù)管理:基于AOP(面向切面編程),事務(wù)管理與業(yè)務(wù)邏輯解耦。兩種實(shí)現(xiàn):(1)在配置文件(xml)中配置。(2)基于@Transactional注解。

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

@Transactional 可以作用在接口、類方法

  • 作用于類:當(dāng)把@Transactional 注解放在類上時,表示所有該類的public方法都配置相同的事務(wù)屬性信息。

  • 作用于方法:當(dāng)類配置了@Transactional,方法也配置了@Transactional,方法的事務(wù)會覆蓋類的事務(wù)配置信息。

  • 作用于接口:不推薦這種使用方法,因?yàn)橐坏?biāo)注在Interface上并且配置了Spring AOP 使用CGLib動態(tài)代理,將會導(dǎo)致@Transactional注解失效

三、@Transactional失效場景

1. 數(shù)據(jù)庫本身不支持

MySql 的 MyISAM 引擎不支持回滾,如果需要自動回滾事務(wù),需要將MySql的引擎設(shè)置成InnoDB;

2. 注解的方法是否為public
//@Transactional注解在private方法上會失效@Transactionalprivate void deleteUser() throws MyException{userMapper.deleteUserA();int i = 1/0;userMapper.deleteUserB();}

idea直接會給出提示Methods annotated with ‘@Transactional’ must be overridable ,原理很簡單,private修飾的方式,spring無法生成動態(tài)代理,AOP代理分別在intercept()和invoke()方法判斷是否進(jìn)行事務(wù)攔截,這兩個方法都會間接調(diào)用AbstractFallbackTransactionAttributeSource類的computeTransactionAttribute方法來獲取事務(wù)控制的相關(guān)屬性。這其中有以下一段代碼

    /**
     * Same signature as {@link #getTransactionAttribute}, but doesn't cache the result.
     * {@link #getTransactionAttribute} is effectively a caching decorator for this method.
     * <p>As of 4.1.8, this method can be overridden.
     * @since 4.1.8
     * @see #getTransactionAttribute
     */
    protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {// Don't allow no-public methods as required.if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}//...
  }

這段代碼會導(dǎo)致no-public的方法無法進(jìn)入事務(wù)控制,所以一定要確保自己需要進(jìn)行事務(wù)控制的方法包含public修飾符。

3. 異常處理不當(dāng)

當(dāng)異常被捕獲后,并且沒有再拋出,那么deleteUserA是不會回滾的,例如:

@Transactionalpublic void deleteUser() {userMapper.deleteUserA();try {int i = 1 / 0;userMapper.deleteUserB();} catch (Exception e) {e.printStackTrace();}}

異步雖然拋出了,但是拋出的是非RuntimeException類型的異常,依舊不會生效,例如:

@Transactionalpublic void deleteUser() throws MyException{userMapper.deleteUserA();try {int i = 1 / 0;userMapper.deleteUserB();} catch (Exception e) {throw new MyException();}}

注解為事務(wù)范圍的方法中,事務(wù)的回滾僅僅對于unchecked的異常有效。對于checked異常無效。也就是說事務(wù)回滾僅僅發(fā)生在,出現(xiàn)RuntimeException或Error的時候。通俗一點(diǎn)就是:代碼中出現(xiàn)的空指針等異常,會被回滾。而文件讀寫、網(wǎng)絡(luò)超時問題等,spring就沒法回滾了。
解決方案:如果指定了回滾異常類型為Exception,那么就可以回滾Checked類型異常了。

@Transactional(rollbackFor = Exception.class)

java里面將派生于Error或者RuntimeException(比如空指針,1/0)的異常稱為unchecked異常,其他繼承自java.lang.Exception得異常統(tǒng)稱為Checked Exception,如IOException、TimeoutException等

4. 方法內(nèi)部直接調(diào)用

如果先調(diào)用deleteUser(),那么deleteUserA()是不會回滾的,其原因就是@Transactional根本沒生成代理,例如:

public void deleteUser() throws MyException{deleteUser2(); // 事物失效}@Transactionalpublic void deleteUser2() throws MyException{userMapper.deleteUserA();int i = 1 / 0;userMapper.deleteUserB();}
5. 多數(shù)據(jù)源事物配置問題

項(xiàng)目中沒有配置事務(wù)管理器,需要在配置類或者配置文件中配置,因?yàn)轫?xiàng)目是多數(shù)據(jù)源的,所以要區(qū)別配置不同數(shù)據(jù)源的事務(wù)管理器,如下:

    @Primary@Bean(name = "db1")public DataSource getDataSource() {return createDataSource();}@Bean(name = "db1TransactionManager")public PlatformTransactionManager txManager(@Qualifier("db1") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}
   @Bean(name = "db2")
   public DataSource getDataSource() {   return buildDataSource();
   }
   @Bean(name = "db2TransactionManager")
   public PlatformTransactionManager txManager(@Qualifier("db2") DataSource dataSource) {   return new DataSourceTransactionManager(dataSource);
   }

可以看到,兩個事務(wù)管理器配置了不同的beanName,接下來只需要 在需要事務(wù)控制的位置加上該事務(wù)管理器的name就可以完美解決!

   @Override
   @Transactional(value = "db1TransactionManager",rollbackFor = Exception.class)
   public int updateOrInsert(BaseRequest<BankTemplateDto> param) {  ...
   }
6. 新開啟一個線程

如下的方式deleteUserA()也不會回滾,因?yàn)閟pring實(shí)現(xiàn)事務(wù)的原理是通過ThreadLocal把數(shù)據(jù)庫連接綁定到當(dāng)前線程中,新開啟一個線程獲取到的連接就不是同一個了,例如:

@Transactionalpublic void deleteUser() throws MyException{userMapper.deleteUserA();try {//休眠1秒,保證deleteUserA先執(zhí)行Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {int i = 1/0;userMapper.deleteUserB();}).start();    }
7. 事務(wù)傳播屬性設(shè)置錯誤

注意傳播屬性的設(shè)置,一般情況下,propagation屬性無需配置。會使用默認(rèn)配置,即:PROPAGATION_REQUIRED,有些propagation屬性會導(dǎo)致事務(wù)不會觸發(fā),一定要注意:
PROPAGATION_SUPPORTS: 如果存在事務(wù),則進(jìn)入事務(wù);否則,以非事務(wù)方式運(yùn)行。
PROPAGATION_NOT_SUPPORTED: 如果存在事務(wù),則掛起事務(wù),并以非事務(wù)方式運(yùn)行。
PROPAGATION_NEVER: 以非事務(wù)形式運(yùn)行,如果存在事務(wù),則拋出異常。

四、@Transactional注有哪些屬性?

propagation屬性

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

  • Propagation.REQUIRED:如果當(dāng)前存在事務(wù),則加入該事務(wù),如果當(dāng)前不存在事務(wù),則創(chuàng)建一個新的事務(wù)。( 也就是說如果A方法和B方法都添加了注解,在默認(rèn)傳播模式下,A方法內(nèi)部調(diào)用B方法,會把兩個方法的事務(wù)合并為一個事務(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)建一個新的事務(wù),如果當(dāng)前存在事務(wù),暫停當(dāng)前的事務(wù)。( 當(dāng)類A中的 a 方法用默認(rèn)Propagation.REQUIRED模式,類B中的 b方法加上采用 Propagation.REQUIRES_NEW模式,然后在 a 方法中調(diào)用 b方法操作數(shù)據(jù)庫,然而 a方法拋出異常后,b方法并沒有進(jìn)行回滾,因?yàn)?code>Propagation.REQUIRES_NEW會暫停 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ù)的隔離級別,默認(rèn)值為 Isolation.DEFAULT。

  • Isolation.DEFAULT:使用底層數(shù)據(jù)庫默認(rèn)的隔離級別。

  • Isolation.READ_UNCOMMITTED

  • Isolation.READ_COMMITTED

  • Isolation.REPEATABLE_READ

  • Isolation.SERIALIZABLE

timeout 屬性

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

readOnly 屬性

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

rollbackFor 屬性

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

noRollbackFor屬性**

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

上述就是小編為大家分享的Spring中@Transactional事務(wù)不生效如何解決了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI