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

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

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

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

背景

前陣子寫一個(gè)項(xiàng)目時(shí),有個(gè)需求是要往3個(gè)庫(kù),3個(gè)表里插入數(shù)據(jù),在同一個(gè)方法里,公司是用baomidou的@DS注解來(lái)實(shí)現(xiàn)配置動(dòng)態(tài)數(shù)據(jù)源的。這是背景,然后呢,我在一個(gè)service方法里,就操作了這三張表,同時(shí),我還加上了@Transactional注解,因?yàn)樵摲椒ㄊ莻€(gè)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(); #報(bào)錯(cuò)
    cMapper.save();
    }    
}

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

@Transactional執(zhí)行流程

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

我們?cè)谏厦媪私獾?,因?yàn)锧Transactional會(huì)創(chuàng)建事務(wù)然后獲得數(shù)據(jù)源,因?yàn)槲覀僺ervice方法上沒(méi)有@DS注解,就拿了默認(rèn)數(shù)據(jù)源,并且在這之后,這個(gè)事務(wù)信息會(huì)通過(guò)threadLocal跟當(dāng)前線程綁定,事務(wù)信息包括了connection連接,也就意味著,在進(jìn)入這個(gè)service方法的時(shí)候,當(dāng)前事務(wù)就綁定了數(shù)據(jù)源a,在運(yùn)行到bMapper.save()時(shí),因?yàn)閏onnection已經(jīng)存在,所以拿到的數(shù)據(jù)源還是a,這時(shí)候就找不到b庫(kù)里的表了。

解決方法

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

pps:

在一個(gè)事務(wù)方法里用this來(lái)調(diào)用另一個(gè)事務(wù)方法時(shí),@DS也會(huì)起作用,原因是this調(diào)用的不是事務(wù)對(duì)象,不會(huì)開(kāi)啟事務(wù)。想具體了解可以看我之前發(fā)的這篇文章 //www.fzitv.net/article/222082.htm

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

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

不多BB,直接上代碼:

public class DataSourceKey {
    /**
     * 用戶數(shù)據(jù)源
     */
    public final static String USER = "userDataSource";
    /**
     * 報(bào)表數(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ù)源開(kāi)始。。。。。。。。。。。。");
        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();
    }
}
/**
 * 動(dòng)態(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();
    }
}
/**
 * 動(dòng)態(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);
 }
    //錯(cuò)誤的代碼,@Transactional注解會(huì)導(dǎo)致 數(shù)據(jù)源切換失敗
    @GetMapping("/testdb")
    @Transactional(rollbackFor = Exception.class)
    public String testDateSources(){  
        userMapper.insertXXXX(xxxx);
        rptFieldMapper.selectXXXX();
        
        return "sss";
    }

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

相關(guān)文章

  • Springboot實(shí)現(xiàn)頁(yè)面間跳轉(zhuǎn)功能

    Springboot實(shí)現(xiàn)頁(yè)面間跳轉(zhuǎn)功能

    這篇文章主要介紹了Springboot實(shí)現(xiàn)頁(yè)面間跳轉(zhuǎn)功能,本文給大家分享兩種方式,方法一和方法二是不沖突的,但是通常情況下如果用方法二addViewControllers,需要把方法一所寫的Controller類給注釋掉,需要的朋友可以參考下
    2023-10-10
  • java讀寫oracle的blob字段示例

    java讀寫oracle的blob字段示例

    這篇文章主要介紹了java讀寫oracle的blob字段示例,需要的朋友可以參考下
    2014-02-02
  • 淺談Mybatis二級(jí)緩存的缺陷

    淺談Mybatis二級(jí)緩存的缺陷

    本文主要介紹了淺談Mybatis二級(jí)緩存的缺陷,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • Java中toString方法的深度解析與應(yīng)用場(chǎng)景詳解

    Java中toString方法的深度解析與應(yīng)用場(chǎng)景詳解

    這篇文章主要介紹了Java中的toString方法及其重寫的重要性和注意事項(xiàng),包括信息的完整性、簡(jiǎn)潔性、格式的統(tǒng)一性、避免性能問(wèn)題和遞歸循環(huán)等問(wèn)題,文中將解決的辦法介紹的非常詳細(xì),需要的朋友可以參考下
    2025-04-04
  • Java中如何計(jì)算一段程序的運(yùn)行時(shí)間

    Java中如何計(jì)算一段程序的運(yùn)行時(shí)間

    這篇文章主要介紹了Java中如何計(jì)算一段程序的運(yùn)行時(shí)間問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 如果你想寫自己的Benchmark框架(推薦)

    如果你想寫自己的Benchmark框架(推薦)

    這篇文章主要介紹了如果你想寫自己的Benchmark框架,本文通過(guò)給大家分享八條軍規(guī),幫助大家理解,需要的朋友可以參考下
    2020-07-07
  • MyBatis映射文件resultMap元素中使用多個(gè)association的方法

    MyBatis映射文件resultMap元素中使用多個(gè)association的方法

    這篇文章主要介紹了MyBatis映射文件resultMap元素中使用多個(gè)association的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Java實(shí)現(xiàn)常用加密算法SM3的方式及測(cè)試代碼

    Java實(shí)現(xiàn)常用加密算法SM3的方式及測(cè)試代碼

    這篇文章介紹了SM3算法在Java中的實(shí)現(xiàn)方式,SM3算法是一種密碼散列函數(shù)標(biāo)準(zhǔn),主要用于商用密碼應(yīng)用中的數(shù)字簽名和驗(yàn)證、消息認(rèn)證碼生成和驗(yàn)證、隨機(jī)數(shù)生成等,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-10-10
  • 基于多線程并發(fā)的常見(jiàn)問(wèn)題(詳解)

    基于多線程并發(fā)的常見(jiàn)問(wèn)題(詳解)

    下面小編就為大家?guī)?lái)一篇基于多線程并發(fā)的常見(jiàn)問(wèn)題(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • Java開(kāi)發(fā)者結(jié)合Node.js編程入門教程

    Java開(kāi)發(fā)者結(jié)合Node.js編程入門教程

    這篇文章主要介紹了Java開(kāi)發(fā)者結(jié)合Node.js編程入門教程,我將先向您展示如何使用Java EE創(chuàng)建一個(gè)簡(jiǎn)單的Rest服務(wù)來(lái)讀取 MongoDB數(shù)據(jù)庫(kù)。然后我會(huì)用node.js來(lái)實(shí)現(xiàn)相同的功能,需要的朋友可以參考下
    2014-09-09

最新評(píng)論

葫芦岛市| 鄂伦春自治旗| 驻马店市| 兴隆县| 阳信县| 高安市| 万全县| 昌平区| 永定县| 鲜城| 淄博市| 明光市| 吴旗县| 扶绥县| 县级市| 同心县| 封丘县| 西宁市| 天镇县| 木里| 宁化县| 霍山县| 双峰县| 邳州市| 竹山县| 清涧县| 青冈县| 永登县| 宜兰市| 长泰县| 棋牌| 米泉市| 潞西市| 阿勒泰市| 龙口市| 天镇县| 汉寿县| 易门县| 定安县| 天气| 荔浦县|