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

關于Spring多數據源TransactionManager沖突的解決方案

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

現象

近期做了一個業(yè)務需求,需要增加多數據源,同時對事務也進行了配置,待發(fā)布上線后出現使用 @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() {
    ... 
}

附多數據源配置示例代碼:

數據源 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>
<!--配置聲明事務-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />

數據源 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>
<!--配置聲明事務-->
<bean id="adsTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="adsDataSource"/>
</bean>
<tx:annotation-driven transaction-manager="adsTransactionManager" />

Spring 事務機制

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

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {
    // 1. 獲取事務屬性,如傳播機制、別名等,事務屬性解析為 RuleBasedTransactionAttribute 實例
    TransactionAttributeSource tas = getTransactionAttributeSource();
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    // 2. 獲取事務管理器
    final TransactionManager tm = determineTransactionManager(txAttr);
    // ......
    PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
        // 3. 聲明式事務處理,判斷條件: txAttr 為空(不是事務) || 事務管理器不是 CallbackPreferringPlatformTransactionManager
        // 創(chuàng)建事務
        TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
        Object retVal;
        try {
            retVal = invocation.proceedWithInvocation(); // 執(zhí)行事務增強方法
        } 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); // 提交事務
        return retVal;
    } else {
        // 4. 編程式事務
        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事務源碼,這里重點分析獲取事務管理器邏輯:

protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
    if (txAttr == null || this.beanFactory == null) {
        return getTransactionManager();
    }
    String qualifier = txAttr.getQualifier();
    if (StringUtils.hasText(qualifier)) {
        // Case 1:事務屬性上配置了 value 值
        return determineQualifiedTransactionManager(this.beanFactory, qualifier);
    } else if (StringUtils.hasText(this.transactionManagerBeanName)) {
        // Case 2:指定了 transactionManagerBeanName
        return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
    } else {
        // Case 3:根據類型獲取注入的 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 函數中獲取事務管理器主要包括三個分支:

Case 1:@Transactional 配置了 value 值

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

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

Case 2:指定了 transactionManagerBeanName

從 Spring 源碼上理解,

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

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

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

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

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

報錯原因及解決方案

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

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

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

總結

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

相關文章

  • SpringBoot自動裝配原理小結

    SpringBoot自動裝配原理小結

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

    java調用chatgpt接口來實現專屬于自己的人工智能助手

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

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

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

    Spring Cloud 服務網關Zuul的實現

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

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

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

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

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

    plantuml畫圖實現代碼畫時序圖UML用例圖

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

    ShardingSphere解析SQL示例詳解

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

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

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

    Java Long類型對比分析

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

最新評論

富宁县| 青河县| 武山县| 哈巴河县| 遂溪县| 济阳县| 绥德县| 巴塘县| 措勤县| 驻马店市| 集安市| 固安县| 石棉县| 丁青县| 江西省| 黔南| 临夏市| 湖北省| 班玛县| 沅江市| 攀枝花市| 顺平县| 贵港市| 宝应县| 甘泉县| 从化市| 叙永县| 黄石市| 合阳县| 融水| 涿鹿县| 怀宁县| 麻城市| 儋州市| 南漳县| 辽宁省| 泰宁县| 石首市| 镇巴县| 荣成市| 休宁县|