溫馨提示×

溫馨提示×

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

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

Spring中@Transactional事務不生效如何解決

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

這期內容當中小編將會給大家帶來有關Spring中@Transactional事務不生效如何解決,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

一、Spring事務管理方式

事務管理在系統開發中是不可缺少的一部分,Spring提供了很好事務管理機制,主要分為編程式事務聲明式事務兩種。

  1. 編碼式事務管理:將事務控制代碼編寫在業務代碼之中。

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

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

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

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

  • 作用于方法:當類配置了@Transactional,方法也配置了@Transactional,方法的事務會覆蓋類的事務配置信息。

  • 作用于接口:不推薦這種使用方法,因為一旦標注在Interface上并且配置了Spring AOP 使用CGLib動態代理,將會導致@Transactional注解失效

三、@Transactional失效場景

1. 數據庫本身不支持

MySql 的 MyISAM 引擎不支持回滾,如果需要自動回滾事務,需要將MySql的引擎設置成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無法生成動態代理,AOP代理分別在intercept()和invoke()方法判斷是否進行事務攔截,這兩個方法都會間接調用AbstractFallbackTransactionAttributeSource類的computeTransactionAttribute方法來獲取事務控制的相關屬性。這其中有以下一段代碼

    /**
     * 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;}//...
  }

這段代碼會導致no-public的方法無法進入事務控制,所以一定要確保自己需要進行事務控制的方法包含public修飾符。

3. 異常處理不當

當異常被捕獲后,并且沒有再拋出,那么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();}}

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

@Transactional(rollbackFor = Exception.class)

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

4. 方法內部直接調用

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

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

項目中沒有配置事務管理器,需要在配置類或者配置文件中配置,因為項目是多數據源的,所以要區別配置不同數據源的事務管理器,如下:

    @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);
   }

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

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

如下的方式deleteUserA()也不會回滾,因為spring實現事務的原理是通過ThreadLocal把數據庫連接綁定到當前線程中,新開啟一個線程獲取到的連接就不是同一個了,例如:

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

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

四、@Transactional注有哪些屬性?

propagation屬性

propagation 代表事務的傳播行為,默認值為 Propagation.REQUIRED,其他的屬性信息如下:

  • Propagation.REQUIRED:如果當前存在事務,則加入該事務,如果當前不存在事務,則創建一個新的事務。( 也就是說如果A方法和B方法都添加了注解,在默認傳播模式下,A方法內部調用B方法,會把兩個方法的事務合并為一個事務

  • Propagation.SUPPORTS:如果當前存在事務,則加入該事務;如果當前不存在事務,則以非事務的方式繼續運行。

  • Propagation.MANDATORY:如果當前存在事務,則加入該事務;如果當前不存在事務,則拋出異常。

  • Propagation.REQUIRES_NEW:重新創建一個新的事務,如果當前存在事務,暫停當前的事務。( 當類A中的 a 方法用默認Propagation.REQUIRED模式,類B中的 b方法加上采用 Propagation.REQUIRES_NEW模式,然后在 a 方法中調用 b方法操作數據庫,然而 a方法拋出異常后,b方法并沒有進行回滾,因為Propagation.REQUIRES_NEW會暫停 a方法的事務 )

  • Propagation.NOT_SUPPORTED:以非事務的方式運行,如果當前存在事務,暫停當前的事務。

  • Propagation.NEVER:以非事務的方式運行,如果當前存在事務,則拋出異常。

  • Propagation.NESTED :和 Propagation.REQUIRED 效果一樣。

isolation 屬性

isolation :事務的隔離級別,默認值為 Isolation.DEFAULT。

  • Isolation.DEFAULT:使用底層數據庫默認的隔離級別。

  • Isolation.READ_UNCOMMITTED

  • Isolation.READ_COMMITTED

  • Isolation.REPEATABLE_READ

  • Isolation.SERIALIZABLE

timeout 屬性

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

readOnly 屬性

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

rollbackFor 屬性

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

noRollbackFor屬性**

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

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

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女