最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

現(xiàn)象

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

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

報錯日志如下:

報錯方法示例:

@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ù)機制

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

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {
    // 1. 獲取事務(wù)屬性,如傳播機制、別名等,事務(wù)屬性解析為 RuleBasedTransactionAttribute 實例
    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ù)增強方法
        } 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ù)源碼,這里重點分析獲取事務(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ù)管理器主要包括三個分支:

Case 1:@Transactional 配置了 value 值

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

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

Case 2:指定了 transactionManagerBeanName

從 Spring 源碼上理解,

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

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

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

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

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

報錯原因及解決方案

了解了 Spring 事務(wù)機制,再來分析問題就比較簡單,根據(jù)上述報錯日志,直接定位到 determineTransactionManager 的 Case 3 情況,說明 Spring 容器中注入了兩個 TransactionManager 

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

  • 解決方式一:因業(yè)務(wù)在數(shù)據(jù)源 adsDateSource 中只有查詢,無寫入操作,所以直接去掉 adsDateSource 事務(wù)配置即可,這樣只有一個 TransactionManager 實例,不會出現(xiàn)類型注入沖突
  • 解決方式二:因為配置了多個數(shù)據(jù)源,在 @Transactional 注解中未指定應(yīng)用哪個數(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é)

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

相關(guān)文章

  • SpringBoot自動裝配原理小結(jié)

    SpringBoot自動裝配原理小結(jié)

    Spring Boot主要作用就是簡化Spring應(yīng)用的開發(fā),開發(fā)者只需要通過少量代碼就可以創(chuàng)建一個Spring應(yīng)用,而達到這一目的最核心的思想就是約定優(yōu)于配置。
    2021-05-05
  • java調(diào)用chatgpt接口來實現(xiàn)專屬于自己的人工智能助手

    java調(diào)用chatgpt接口來實現(xiàn)專屬于自己的人工智能助手

    這篇文章主要介紹了用java來調(diào)用chatget的接口,實現(xiàn)自己的聊天機器人,對人工智能感興趣的小伙伴可以參考閱讀
    2023-03-03
  • Java分層開發(fā)必知之PO、BO、DTO、VO、POJO概念實例詳解

    Java分層開發(fā)必知之PO、BO、DTO、VO、POJO概念實例詳解

    Java編程模型是一種在軟件開發(fā)中幫助組織和管理代碼的框架,其中,VO,BO,PO,DO,以及DTO是一些關(guān)鍵概念,它們在不同層次和階段起到重要作用,這篇文章主要介紹了Java分層開發(fā)必知之PO、BO、DTO、VO、POJO概念的相關(guān)資料,需要的朋友可以參考下
    2026-02-02
  • Spring Cloud 服務(wù)網(wǎng)關(guān)Zuul的實現(xiàn)

    Spring Cloud 服務(wù)網(wǎng)關(guān)Zuul的實現(xiàn)

    這篇文章主要介紹了Spring Cloud 服務(wù)網(wǎng)關(guān)Zuul的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • 詳解Java中的三種流程控制語句

    詳解Java中的三種流程控制語句

    這篇文章主要介紹了Java中常用的三種流程控制語句的使用,文中講解非常細致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2022-04-04
  • 一文讓你徹底明白Java中的值傳遞和引用傳遞

    一文讓你徹底明白Java中的值傳遞和引用傳遞

    這篇文章主要給大家介紹了關(guān)于Java中值傳遞和引用傳遞的相關(guān)資料,值傳遞是指在調(diào)用函數(shù)時將實際參數(shù)復(fù)制一份傳遞到函數(shù)中,引用傳遞是指在調(diào)用函數(shù)時將實際參數(shù)的引用直接傳遞到函數(shù)中,需要的朋友可以參考下
    2023-10-10
  • plantuml畫圖實現(xiàn)代碼畫時序圖UML用例圖

    plantuml畫圖實現(xiàn)代碼畫時序圖UML用例圖

    這篇文章主要為大家介紹了plantuml畫圖實現(xiàn)代碼畫時序圖示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • ShardingSphere解析SQL示例詳解

    ShardingSphere解析SQL示例詳解

    這篇文章主要為大家介紹了ShardingSphere解析SQL的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • SpringBoot如何使用注解進行XSS防御

    SpringBoot如何使用注解進行XSS防御

    在SpringBoot中,可以通過自定義@XSS注解和實現(xiàn)XSSValidator類來防御XSS攻擊,此方法適用于GET和POST請求,通過在方法參數(shù)或?qū)嶓w類屬性上添加@XSS注解,并結(jié)合@Valid或@Validated注解使用,有效攔截潛在的XSS腳本,保障應(yīng)用安全
    2024-11-11
  • Java Long類型對比分析

    Java Long類型對比分析

    這篇文章主要介紹了Java Long類型對比分析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07

最新評論

新巴尔虎左旗| 建昌县| 武山县| 芮城县| 辽中县| 安图县| 泰来县| 铁岭县| 廊坊市| 阿拉善盟| 宁乡县| 岚皋县| 岐山县| 鲁甸县| 大邑县| 巴南区| 云龙县| 阳东县| 阳春市| 麻城市| 桂平市| 安宁市| 莱阳市| 南皮县| 万盛区| 五原县| 安顺市| 阿鲁科尔沁旗| 鸡西市| 灵台县| 抚远县| 福泉市| 柘荣县| 富川| 禹城市| 瑞昌市| 托里县| 灌阳县| 莲花县| 全州县| 武安市|