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

@Transactional跟@DS動態(tài)數(shù)據(jù)源注解沖突的解決

 更新時間:2021年09月07日 10:42:48   作者:林蝸牛snail  
這篇文章主要介紹了@Transactional跟@DS動態(tài)數(shù)據(jù)源注解沖突的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

@Transactional跟@DS動態(tài)數(shù)據(jù)源注解沖突

背景

前陣子寫一個項目時,有個需求是要往3個庫,3個表里插入數(shù)據(jù),在同一個方法里,公司是用baomidou的@DS注解來實現(xiàn)配置動態(tài)數(shù)據(jù)源的。這是背景,然后呢,我在一個service方法里,就操作了這三張表,同時,我還加上了@Transactional注解,因為該方法是個save方法,我就加上了事務(wù)。

偽代碼如下:

spring:
  datasource:
    dynamic:
      primary: A 
      datasource:
        A:
          url:..
          driver-class-name: com.mysql.jdbc.Driver
          username: root
          password:
        B:
          url: ..
          driver-class-name: com.mysql.jdbc.Driver
          username: root
          password:
        C:
          url: ..
          driver-class-name: com.mysql.jdbc.Driver
          username: root
          password:
@Mapper
@DS("A")
public interface AMapper{
      @Insert("insert into a ...")
      void save();
}
@Mapper
@DS("B")
public interface BMapper{
      @Insert("insert into b ...")
      void save();
}
@Mapper
@DS("C")
public interface CMapper{
      @Insert("insert into c ...")
      void save();
}
public class aService{ 
@Autowired
private aMapper、bMapper、cMapper
 
    @Transactional
    public void save(){
    aMapper.save();
    bMapper.save(); #報錯
    cMapper.save();
    }    
}

在開發(fā)完成用postman自測時,發(fā)現(xiàn)bMapper.save()那一行報錯了,報錯內(nèi)容:找不到b表。如果把@Transactional注釋掉,代碼正常運行,數(shù)據(jù)成功落庫。我們明明在3個mapper上面都加了@DS注解來切換數(shù)據(jù)源,那為啥加了@Transactional就不行了呢?

@Transactional執(zhí)行流程

  • save方法添加了 @Transactional 注解,Spring 事務(wù)就會生效。此時,Spring TransactionInterceptor 會通過 AOP 攔截該方法,創(chuàng)建事務(wù)。而創(chuàng)建事務(wù),勢必就會獲得數(shù)據(jù)源。那么,TransactionInterceptor 會使用 Spring DataSourceTransactionManager 創(chuàng)建事務(wù),并將事務(wù)信息通過 ThreadLocal 綁定在當(dāng)前線程。
  • 而事務(wù)信息,就包括事務(wù)對應(yīng)的 Connection 連接。那也就意味著,還沒走到 OrderMapper 的查詢操作,Connection 就已經(jīng)被創(chuàng)建出來了。并且,因為事務(wù)信息會和當(dāng)前線程綁定在一起,在 OrderMapper 在查詢操作需要獲得 Connection 時,就直接拿到當(dāng)前線程綁定的 Connection ,而不是 OrderMapper 添加 @DS 注解所對應(yīng)的 DataSource 所對應(yīng)的 Connection 。
  • OK ,那么我們現(xiàn)在可以把問題聚焦到 DataSourceTransactionManager 是怎么獲取 DataSource 從而獲得 Connection 的了。對于每個 DataSourceTransactionManager 數(shù)據(jù)庫事務(wù)管理器,創(chuàng)建時都會傳入其需要管理的 DataSource 數(shù)據(jù)源。在使用 dynamic-datasource-spring-boot-starter 時,它創(chuàng)建了一個 DynamicRoutingDataSource ,傳入到 DataSourceTransactionManager 中。
  • 而 DynamicRoutingDataSource 負(fù)責(zé)管理我們配置的多個數(shù)據(jù)源。例如說,本示例中就管理了 a、b、c 三個數(shù)據(jù)源,并且默認(rèn)使用 a 數(shù)據(jù)源。那么在當(dāng)前場景下,DynamicRoutingDataSource 需要基于 @DS 獲得數(shù)據(jù)源名,從而獲得對應(yīng)的 DataSource ,結(jié)果因為我們在 Service 方法上,并沒有添加 @DS 注解,所以它只好返回默認(rèn)數(shù)據(jù)源,也就是 a 。故此,就發(fā)生了 找不到表 的異常。

我們在上面了解到,因為@Transactional會創(chuàng)建事務(wù)然后獲得數(shù)據(jù)源,因為我們service方法上沒有@DS注解,就拿了默認(rèn)數(shù)據(jù)源,并且在這之后,這個事務(wù)信息會通過threadLocal跟當(dāng)前線程綁定,事務(wù)信息包括了connection連接,也就意味著,在進(jìn)入這個service方法的時候,當(dāng)前事務(wù)就綁定了數(shù)據(jù)源a,在運行到bMapper.save()時,因為connection已經(jīng)存在,所以拿到的數(shù)據(jù)源還是a,這時候就找不到b庫里的表了。

解決方法

把這三個入庫操作分為3個獨立的方法,并且都加上@Transactional和 @DS注解(在service上加)。ps:在完成了aMapper.save()之后去調(diào)用bMapper.save()時,一定要把@Transactional設(shè)置為Propagation.REQUIRES_NEW,這樣在調(diào)用另一個事務(wù)方法時,TransactionInterceptor 會將原事務(wù)掛起,暫時性的將原事務(wù)信息和當(dāng)前線程解綁。

pps:

在一個事務(wù)方法里用this來調(diào)用另一個事務(wù)方法時,@DS也會起作用,原因是this調(diào)用的不是事務(wù)對象,不會開啟事務(wù)。想具體了解可以看我之前發(fā)的這篇文章 //www.dbjr.com.cn/article/222082.htm

動態(tài)數(shù)據(jù)源切換失敗

由事務(wù)@Transactional注解導(dǎo)致動態(tài)數(shù)據(jù)源切換失效的問題

不多BB,直接上代碼:

public class DataSourceKey {
    /**
     * 用戶數(shù)據(jù)源
     */
    public final static String USER = "userDataSource";
    /**
     * 報表數(shù)據(jù)源
     */
    public final static String REPORT = "reportDataSource";
    /**
     * 所有數(shù)據(jù)源的集合
     */
    final static List<String> SOURCES = ImmutableList.of(USER, REPORT);
    /**
     * 根據(jù)包名找到數(shù)據(jù)源, 多數(shù)據(jù)源的前綴不能存在相同的。 例: user -> userDataSource
     *
     * @param pack 包名
     * @return 數(shù)據(jù)源名
     */
    public static String getDataSourceKey(String pack) {
        return SOURCES.stream().filter(s -> s.startsWith(pack)).findFirst().orElse(USER);
    }
}
@Component
@Aspect
@Order(-1)
@Slf4j
public class DynamicDataSourceAspect {
    @Pointcut("execution(* com.in.g.data.mapper..*.*(..))")
    public void dataSourcePointcut() {
    }
    @Before("dataSourcePointcut()")
    public void doBefore(JoinPoint point) throws Throwable {
        log.debug("切換數(shù)據(jù)源開始。。。。。。。。。。。。");
        Package pack = point.getSignature().getDeclaringType().getPackage();
        String str = StringUtils.substringAfterLast(pack.getName(), ".");
        String dataSourceKey = DataSourceKey.getDataSourceKey(str);
        DynamicDataSourceHolder.set(dataSourceKey);
        log.debug("切換數(shù)據(jù)源成功,當(dāng)前數(shù)據(jù)源:{}", dataSourceKey);
    }
    @After("dataSourcePointcut()")
    public void doAfterReturning() throws Throwable {
        DynamicDataSourceHolder.clear();
    }
}
/**
 * 動態(tài)數(shù)據(jù)源持有者
 */
public class DynamicDataSourceHolder {
    public static ThreadLocal<String> keyHolder = new ThreadLocal<>();
    public static void clear() {
        keyHolder.remove();
    }
    public static void set(String key) {
        keyHolder.set(key);
    }
    public static String get() {
        return keyHolder.get();
    }
}
/**
 * 動態(tài)數(shù)據(jù)源配置
 */
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceHolder.get();
    }
}
//正確的代碼   --這里偷懶了,直接controller調(diào)用dao層
@GetMapping("/testdb")
    public String testDateSources(){
     //縮小事務(wù)的范圍
       add();
       rptFieldMapper.selectxxxx();
       
       return "sss";
    }
    @Transactional(rollbackFor = Exception.class)
    public void add() {
     userMapper.insertXXXX(xxxx);
 }
    //錯誤的代碼,@Transactional注解會導(dǎo)致 數(shù)據(jù)源切換失敗
    @GetMapping("/testdb")
    @Transactional(rollbackFor = Exception.class)
    public String testDateSources(){  
        userMapper.insertXXXX(xxxx);
        rptFieldMapper.selectXXXX();
        
        return "sss";
    }

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

相關(guān)文章

  • Mybatis中的常用OGNL表達(dá)式

    Mybatis中的常用OGNL表達(dá)式

    這篇文章主要介紹了Mybatis中的常用OGNL表達(dá)式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • 解決SpringBoot @value注解取不到值的問題

    解決SpringBoot @value注解取不到值的問題

    這篇文章主要介紹了解決SpringBoot @value注解取不到值的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Spring Boot 2.2 正式發(fā)布,大幅性能提升 + Java 13 支持

    Spring Boot 2.2 正式發(fā)布,大幅性能提升 + Java 13 支持

    隨著 Spring Framework 5.2.0 成功發(fā)布之后,Spring Boot 2.2 也緊跟其后,發(fā)布了第一個版本:2.2.0。下面就來一起來看看這個版本都更新了些什么值得我們關(guān)注的內(nèi)容
    2019-10-10
  • Java實現(xiàn)簡單控制臺版ATM系統(tǒng)

    Java實現(xiàn)簡單控制臺版ATM系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)簡單控制臺版ATM系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • 怎么把本地jar包放入本地maven倉庫和遠(yuǎn)程私服倉庫

    怎么把本地jar包放入本地maven倉庫和遠(yuǎn)程私服倉庫

    這篇文章主要介紹了怎么把本地jar包放入本地maven倉庫和遠(yuǎn)程私服倉庫的相關(guān)知識,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-06-06
  • IDEA 必要配置設(shè)置方式

    IDEA 必要配置設(shè)置方式

    這篇文章主要介紹了IDEA 必要配置設(shè)置方式,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-04-04
  • nacos中的配置使用@Value注解獲取不到值的原因及解決方案

    nacos中的配置使用@Value注解獲取不到值的原因及解決方案

    這篇文章主要介紹了nacos中的配置使用@Value注解獲取不到值的原因分析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-03-03
  • springboot打包jar中沒有主清單屬性問題

    springboot打包jar中沒有主清單屬性問題

    這篇文章主要介紹了springboot打包jar中沒有主清單屬性問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • JSP安全開發(fā)之XSS漏洞詳解

    JSP安全開發(fā)之XSS漏洞詳解

    XSS又叫CSS (Cross Site Script) ,跨站腳本攻擊。它指的是惡意攻擊者往Web頁面里插入惡意腳本代碼,而程序?qū)τ谟脩糨斎雰?nèi)容未過濾,當(dāng)用戶瀏覽該頁之時,嵌入其中Web里面的腳本代碼會被執(zhí)行,從而達(dá)到惡意攻擊用戶的特殊目的。
    2016-09-09
  • SpringBoot實現(xiàn)接口防刷的兩種方法

    SpringBoot實現(xiàn)接口防刷的兩種方法

    接口被刷指的是同一接口被頻繁調(diào)用,可能是由于以下原因?qū)е拢簮阂夤艉驼`操作或程序錯誤,本文給大家介紹了SpringBoot實現(xiàn)接口防刷的兩種方法,并有相關(guān)的代碼示例供大家參考,需要的朋友可以參考下
    2024-06-06

最新評論