欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

解決@Transaction注解導致動態(tài)切換更改數(shù)據(jù)庫失效問題

 更新時間:2021年09月07日 09:57:34   作者:侖小杰  
這篇文章主要介紹了解決@Transaction注解導致動態(tài)切換更改數(shù)據(jù)庫失效問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

@Transaction注解導致動態(tài)切換更改數(shù)據(jù)庫失效

使用場景

  • 給所有的Controller方法上加切點
  • 在@Before注解的方法里,根據(jù)http請求中攜帶的header,動態(tài)切換數(shù)據(jù)源
  • 使用mybatis或者jpa執(zhí)行操作

遇到問題

當給Controller方法加上@Transaction注解后,動態(tài)切換數(shù)據(jù)源就失效了,原因是每次@Before注解的方法運行之前,protected abstract Object determineCurrentLookupKey();就已經(jīng)運行了,而這個方法是切換數(shù)據(jù)源的關(guān)鍵。

解決

其實也算不上解決,就是不要在Controller方法上加事務注解,非要加事務,中間的Service層就不要省了。

@Transactional失效的場景及原理

1.@Transactional修飾的方法

為非public方法,這個時候@Transactional會實現(xiàn)。

失敗的原理是:@Transactional是基于動態(tài)代理來實現(xiàn)的,非public的方法,他@Transactional的動態(tài)代理對象信息為空,所以不能回滾。

2.在類內(nèi)部沒有添加@Transactional的方法

調(diào)用了@Transactional方法時,當你調(diào)用是,他也不會回滾

測試代碼如下

@Service
public class UserServiceImpl extends BaseServiceImpl<UserEntity> implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    @Transactional
    public void insertOne() {
        UserEntity userEntity = new UserEntity();
        userEntity.setUsername("Michael_C_2019");
        //插入到數(shù)據(jù)庫
        userMapper.insertSelective(userEntity);
        //手動拋出異常
        throw new IndexOutOfBoundsException();
    }
    @Override
    public void saveOne() {
        insertOne();
    }
}

失敗的原理:@Transactional是基于動態(tài)代理對象來實現(xiàn)的,而在類內(nèi)部的方法的調(diào)用是通過this關(guān)鍵字來實現(xiàn)的,沒有經(jīng)過動態(tài)代理對象,所以事務回滾失效。

3.就是在@Transactional方法內(nèi)部捕獲了異常

沒有在catch代碼塊里面重新拋出異常,事務也不會回滾。

代碼如下:

@Override
    @Transactional
    public void insertOne() {
        try {
            UserEntity userEntity = new UserEntity();
            userEntity.setUsername("Michael_C_2019");
            //插入到數(shù)據(jù)庫
            userMapper.insertSelective(userEntity);
            //手動拋出異常
            throw new IndexOutOfBoundsException();
        } catch (IndexOutOfBoundsException e) {
            e.printStackTrace();
        }
    }

所以在阿里巴巴的Java開發(fā)者手冊里面有明確規(guī)定,在 @Transactional的方法里面捕獲了異常,必須要手動回滾,

代碼如下:

 @Override
    @Transactional
    public void insertOne() {
        try {
            UserEntity userEntity = new UserEntity();
            userEntity.setUsername("Michael_C_2019");
            //插入到數(shù)據(jù)庫
            userMapper.insertSelective(userEntity);
            //手動拋出異常
            throw new IndexOutOfBoundsException();
        } catch (IndexOutOfBoundsException e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

失敗原理:這時候我們來看看spring的源碼:

TransactionAspectSupport類里面的invokeWithinTransaction方法

TransactionAspectSupport
@Nullable
    protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, TransactionAspectSupport.InvocationCallback invocation) throws Throwable {
        TransactionAttributeSource tas = this.getTransactionAttributeSource();
        TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null;
        PlatformTransactionManager tm = this.determineTransactionManager(txAttr);
        String joinpointIdentification = this.methodIdentification(method, targetClass, txAttr);
        Object result;
        if (txAttr != null && tm instanceof CallbackPreferringPlatformTransactionManager) {
            TransactionAspectSupport.ThrowableHolder throwableHolder = new TransactionAspectSupport.ThrowableHolder(null);
            try {
                result = ((CallbackPreferringPlatformTransactionManager)tm).execute(txAttr, (status) -> {
                    TransactionAspectSupport.TransactionInfo txInfo = this.prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                    Object var9;
                    try {
                        Object var8 = invocation.proceedWithInvocation();
                        return var8;
                    } catch (Throwable var13) {
                        if (txAttr.rollbackOn(var13)) {
                            if (var13 instanceof RuntimeException) {
                                throw (RuntimeException)var13;
                            }
                            throw new TransactionAspectSupport.ThrowableHolderException(var13);
                        }
                        throwableHolder.throwable = var13;
                        var9 = null;
                    } finally {
                        this.cleanupTransactionInfo(txInfo);
                    }
                    return var9;
                });
                if (throwableHolder.throwable != null) {
                    throw throwableHolder.throwable;
                } else {
                    return result;
                }
            } catch (TransactionAspectSupport.ThrowableHolderException var19) {
                throw var19.getCause();
            } catch (TransactionSystemException var20) {
                if (throwableHolder.throwable != null) {
                    this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                    var20.initApplicationException(throwableHolder.throwable);
                }
                throw var20;
            } catch (Throwable var21) {
                if (throwableHolder.throwable != null) {
                    this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                }
                throw var21;
            }
        } else {
            TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
            result = null;
            try {
                result = invocation.proceedWithInvocation();
            } catch (Throwable var17) {
              //異常時,在catch邏輯中回滾事務
                this.completeTransactionAfterThrowing(txInfo, var17);
                throw var17;
            } finally {
                this.cleanupTransactionInfo(txInfo);
            }
            this.commitTransactionAfterReturning(txInfo);
            return result;
        }
    }

他是通過捕獲異常然后在catch里面進行事務的回滾的,所以如果你在自己的方法里面catch了異常,catch里面沒有拋出新的異常,那么事務將不會回滾。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • MyBatis的foreach語句詳解

    MyBatis的foreach語句詳解

    這篇文章主要介紹了MyBatis的foreach語句詳解的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-06-06
  • Java設計模式之迭代器模式解析

    Java設計模式之迭代器模式解析

    這篇文章主要介紹了Java設計模式之迭代器模式解析,迭代器模式提供一個對象來順序訪問聚合對象中的一系列數(shù)據(jù),而不暴露聚合對象的內(nèi)部表示,本文提供了部分代碼,需要的朋友可以參考下
    2023-09-09
  • Java中StringBuilder與StringBuffer的區(qū)別

    Java中StringBuilder與StringBuffer的區(qū)別

    在Java編程中,字符串的拼接是一項常見的操作。為了有效地處理字符串的拼接需求,Java提供了兩個主要的類:StringBuilder和StringBuffer,本文主要介紹了Java中StringBuilder與StringBuffer的區(qū)別,感興趣的可以了解一下
    2023-08-08
  • 一篇文章帶你了解jdk1.8新特性--為什么使用lambda表達式

    一篇文章帶你了解jdk1.8新特性--為什么使用lambda表達式

    Lambda是一個匿名函數(shù),我們可以把Lambda表達式理解為是一段可以傳遞的代碼,本篇文章就帶你了解,希望能給你帶來幫助
    2021-08-08
  • Java定時器例子_動力節(jié)點Java學院整理

    Java定時器例子_動力節(jié)點Java學院整理

    本文給大家分享了java定時器例子,非常不錯,具有參考借鑒價值,需要的的朋友參考下吧
    2017-05-05
  • Springboot啟用多個監(jiān)聽端口代碼實例

    Springboot啟用多個監(jiān)聽端口代碼實例

    這篇文章主要介紹了Springboot啟用多個監(jiān)聽端口代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-06-06
  • Java經(jīng)典面試題最全匯總208道(五)

    Java經(jīng)典面試題最全匯總208道(五)

    這篇文章主要介紹了Java經(jīng)典面試題最全匯總208道(五),本文章內(nèi)容詳細,該模塊分為了六個部分,本次為第五部分,需要的朋友可以參考下
    2023-01-01
  • Java靜態(tài)代理與動態(tài)代理案例詳解

    Java靜態(tài)代理與動態(tài)代理案例詳解

    這篇文章主要介紹了Java靜態(tài)代理與動態(tài)代理案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • Java 線程死鎖的問題解決辦法

    Java 線程死鎖的問題解決辦法

    這篇文章主要介紹了 Java 線程死鎖的問題解決辦法的相關(guān)資料,希望通過本大家能幫助到大家,遇到類似問題能夠解決,需要的朋友可以參考下
    2017-09-09
  • SpringBoot快速搭建web項目詳細步驟總結(jié)

    SpringBoot快速搭建web項目詳細步驟總結(jié)

    這篇文章主要介紹了SpringBoot快速搭建web項目詳細步驟總結(jié) ,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-12-12

最新評論