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

關(guān)于Spring多數(shù)據(jù)源TransactionManager沖突的解決方案

 更新時(shí)間:2023年07月06日 14:20:52   作者:bboyzqh  
這篇文章主要介紹了關(guān)于Spring多數(shù)據(jù)源TransactionManager沖突的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

現(xiàn)象

近期做了一個(gè)業(yè)務(wù)需求,需要增加多數(shù)據(jù)源,同時(shí)對(duì)事務(wù)也進(jìn)行了配置,待發(fā)布上線后出現(xiàn)使用 @Transactional 注解的方法拋出 NoUniqueBeanDefinitionException 異常:

No qualifying bean of type ‘org.springframework.transaction.PlatformTransactionManager’ available: expected single matching bean but found 2: adsTransactionManager,transactionManager,

報(bào)錯(cuò)日志如下:

報(bào)錯(cuò)方法示例:

@Transactional
public void generateFreezeBondId() {
    ... 
}

附多數(shù)據(jù)源配置示例代碼:

數(shù)據(jù)源 dataSource

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
      <property name="driverClassName" value="${jdbc.driverClassName}" />
      <property name="url" value="${jdbc.url}"/>
      <property name="username" value="${jdbc.username}"/>
      <property name="password" value="${jdbc.password}"/>
</bean> 
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
      <property name="dataSource" ref="dataSource"/>
      <property name="configLocation" value="classpath:mybatis.xml"/>
      <property name="mapperLocations" value="classpath:com/dao/*.xml"/>
</bean>
<!-- Mapper接口組件掃描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="cn.zqh.dao"/>
</bean>
<!--配置聲明事務(wù)-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />

數(shù)據(jù)源 adsDataSource

<bean id="adsDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
      <property name="driverClassName" value="${ads.jdbc.driverClassName}" />
      <property name="url" value="${ads.jdbc.url}"/>
      <property name="username" value="${ads.jdbc.username}"/>
      <property name="password" value="${ads.jdbc.password}"/>
</bean> 
<bean id="adsSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
      <property name="dataSource" ref="adsDataSource"/>
      <property name="configLocation" value="classpath:ads/mybatis.xml"/>
      <property name="mapperLocations" value="classpath:com/ads/dao/*.xml"/>
</bean>
<!-- Mapper接口組件掃描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="cn.zqh.ads.dao"/>
</bean>
<!--配置聲明事務(wù)-->
<bean id="adsTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="adsDataSource"/>
</bean>
<tx:annotation-driven transaction-manager="adsTransactionManager" />

Spring 事務(wù)機(jī)制

首先結(jié)合 Spring 源碼來(lái)分析下 Spring 的事務(wù)執(zhí)行機(jī)制,核心代碼如下(org.springframework.transaction.interceptor.TransactionAspectSupport):

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {
    // 1. 獲取事務(wù)屬性,如傳播機(jī)制、別名等,事務(wù)屬性解析為 RuleBasedTransactionAttribute 實(shí)例
    TransactionAttributeSource tas = getTransactionAttributeSource();
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    // 2. 獲取事務(wù)管理器
    final TransactionManager tm = determineTransactionManager(txAttr);
    // ......
    PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
        // 3. 聲明式事務(wù)處理,判斷條件: txAttr 為空(不是事務(wù)) || 事務(wù)管理器不是 CallbackPreferringPlatformTransactionManager
        // 創(chuàng)建事務(wù)
        TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
        Object retVal;
        try {
            retVal = invocation.proceedWithInvocation(); // 執(zhí)行事務(wù)增強(qiáng)方法
        } catch (Throwable ex) {
            completeTransactionAfterThrowing(txInfo, ex);  // 異常回滾
            throw ex;
        } finally {
            cleanupTransactionInfo(txInfo);
        }
        if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
            TransactionStatus status = txInfo.getTransactionStatus();
            if (status != null && txAttr != null) {
                retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
            }
        }
        commitTransactionAfterReturning(txInfo); // 提交事務(wù)
        return retVal;
    } else {
        // 4. 編程式事務(wù)
        Object result;
        final ThrowableHolder throwableHolder = new ThrowableHolder();
        result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
            TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
            try {
                Object retVal = invocation.proceedWithInvocation();
                if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) 
                    retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
            }
            return retVal;
        } catch (Throwable ex) {
            //.......
        });
        // ......
        return result;
    }
}

主流程比較清晰,有興趣可參考Spring事務(wù)源碼,這里重點(diǎn)分析獲取事務(wù)管理器邏輯:

protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
    if (txAttr == null || this.beanFactory == null) {
        return getTransactionManager();
    }
    String qualifier = txAttr.getQualifier();
    if (StringUtils.hasText(qualifier)) {
        // Case 1:事務(wù)屬性上配置了 value 值
        return determineQualifiedTransactionManager(this.beanFactory, qualifier);
    } else if (StringUtils.hasText(this.transactionManagerBeanName)) {
        // Case 2:指定了 transactionManagerBeanName
        return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
    } else {
        // Case 3:根據(jù)類型獲取注入的 TransactionManager
        TransactionManager defaultTransactionManager = getTransactionManager();
        if (defaultTransactionManager == null) {
            defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
            if (defaultTransactionManager == null) {
                defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);
                this.transactionManagerCache.putIfAbsent(DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
            }
        }
        return defaultTransactionManager;
    }
}

determineTransactionManager 函數(shù)中獲取事務(wù)管理器主要包括三個(gè)分支:

Case 1:@Transactional 配置了 value 值

public @interface Transactional {
	@AliasFor("transactionManager")
	String value() default "";
	@AliasFor("value")
	String transactionManager() default "";
    //......
}

spring 在解析注解 @Transactional 的時(shí)候,會(huì)將 value 的值寫入到 qualifier 中,會(huì)根據(jù) qualifier 來(lái)獲取事務(wù)管理器

Case 2:指定了 transactionManagerBeanName

從 Spring 源碼上理解,

<tx:annotation-driven transaction-manager="transactionManager"/> 

會(huì)在解析該標(biāo)簽時(shí)將屬性 transaction-manager 的值設(shè)置到 TransactionInterceptor 的父類 TransactionAspectSupporttransactionManagerBeanName 屬性中(本質(zhì)上是生成 TransactionInterceptor Bean 實(shí)例),這里可參考方法:

org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser.AopAutoProxyConfigurer#configureAutoProxyCreator

從業(yè)務(wù)代碼配置上看,兩個(gè)數(shù)據(jù)源都指定了 transactionManagerBeanName,即使隨機(jī)加載一個(gè)也應(yīng)該會(huì)找到相應(yīng)的 TransactionManager,所以這里就不太明白為什么在事務(wù)攔截器執(zhí)行的時(shí)候獲取不到 transactionManagerBeanName,留給后面做個(gè)研究。

Case 3:除了上述兩種 case,其他情況會(huì)根據(jù)類型獲取注入的 TransactionManager

報(bào)錯(cuò)原因及解決方案

了解了 Spring 事務(wù)機(jī)制,再來(lái)分析問(wèn)題就比較簡(jiǎn)單,根據(jù)上述報(bào)錯(cuò)日志,直接定位到 determineTransactionManager 的 Case 3 情況,說(shuō)明 Spring 容器中注入了兩個(gè) TransactionManager 

所以常用解決方案有以下幾種

  • 解決方式一:因業(yè)務(wù)在數(shù)據(jù)源 adsDateSource 中只有查詢,無(wú)寫入操作,所以直接去掉 adsDateSource 事務(wù)配置即可,這樣只有一個(gè) TransactionManager 實(shí)例,不會(huì)出現(xiàn)類型注入沖突
  • 解決方式二:因?yàn)榕渲昧硕鄠€(gè)數(shù)據(jù)源,在 @Transactional 注解中未指定應(yīng)用哪個(gè)數(shù)據(jù)源,所以直接指定數(shù)據(jù)源即,示例如下:
// Step 1:配置數(shù)據(jù)源指定 Qualifier
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
    <qualifier value = "dataSourceQualifier"/>
</bean>
// Step 2:修改事務(wù)屬性配置
@Transactional("dataSourceQualifier")
public void generateFreezeBondId() {
    ... 
}

總結(jié)

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

相關(guān)文章

  • Java異常學(xué)習(xí)之自定義異常詳解

    Java異常學(xué)習(xí)之自定義異常詳解

    你的程序總有一天會(huì)崩潰掉,在崩潰掉的時(shí)候我們要知道它在哪,為了什么而崩潰掉,數(shù)據(jù)的保存或者丟失情況如何等問(wèn)題。下面這篇文章主要給大家介紹了關(guān)于Java異常學(xué)習(xí)之自定義異常的相關(guān)資料,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。
    2017-06-06
  • java網(wǎng)上圖書商城(7)訂單模塊2

    java網(wǎng)上圖書商城(7)訂單模塊2

    這篇文章主要為大家詳細(xì)介紹了java網(wǎng)上圖書商城,訂單模塊第二篇,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • Java 中HttpURLConnection附件上傳的實(shí)例詳解

    Java 中HttpURLConnection附件上傳的實(shí)例詳解

    這篇文章主要介紹了Java 中HttpURLConnection附件上傳的實(shí)例詳解的相關(guān)資料,希望通過(guò)本文大家能掌握這樣的知識(shí)內(nèi)容,需要的朋友可以參考下
    2017-09-09
  • JMX監(jiān)控的具體使用

    JMX監(jiān)控的具體使用

    JMX最常見的場(chǎng)景是監(jiān)控Java程序的基本信息和運(yùn)行情況,本文主要介紹了JMX監(jiān)控的具體使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • 詳解SpringMVC解決跨域的兩種方案

    詳解SpringMVC解決跨域的兩種方案

    本篇文章主要介紹了詳解SpringMVC解決跨域的兩種方案,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-08-08
  • Spring對(duì)事務(wù)管理的支持

    Spring對(duì)事務(wù)管理的支持

    今天小編就為大家分享一篇關(guān)于Spring對(duì)事務(wù)管理的支持,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-01-01
  • java?SpringMvc中攔截器的應(yīng)用

    java?SpringMvc中攔截器的應(yīng)用

    大家好,本篇文章主要講的是java?SpringMvc中攔截器的應(yīng)用,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-01-01
  • java源碼閱讀之java.lang.Object

    java源碼閱讀之java.lang.Object

    這篇文章主要介紹了java源碼閱讀之java.lang.Object,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • IDEA快速生成實(shí)體類的示例教程

    IDEA快速生成實(shí)體類的示例教程

    這篇文章主要介紹了IDEA快速生成實(shí)體類的示例教程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • Java中volatile防止指令重排

    Java中volatile防止指令重排

    volatile可以防止指令重排,在多線程環(huán)境下有時(shí)候我們需要使用volatile來(lái)防止指令重排,來(lái)保證代碼運(yùn)行后數(shù)據(jù)的準(zhǔn)確性,感興趣的小伙伴們可以參考一下
    2021-08-08

最新評(píng)論