亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java中@Transactional跟@DS動態數據源注解沖突問題怎么解決

發布時間:2021-09-07 13:51:54 來源:億速云 閱讀:634 作者:chen 欄目:開發技術

本篇內容介紹了“java中@Transactional跟@DS動態數據源注解沖突問題怎么解決”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

目錄
  • @Transactional跟@DS動態數據源注解沖突

    • 背景

    • @Transactional執行流程

    • 解決方法

  • 動態數據源切換失敗

    • 由事務@Transactional注解導致動態數據源切換失效的問題

@Transactional跟@DS動態數據源注解沖突

背景

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

偽代碼如下:

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();
    }    
}

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

@Transactional執行流程

  • save方法添加了 @Transactional 注解,Spring 事務就會生效。此時,Spring TransactionInterceptor 會通過 AOP 攔截該方法,創建事務。而創建事務,勢必就會獲得數據源。那么,TransactionInterceptor 會使用 Spring DataSourceTransactionManager 創建事務,并將事務信息通過 ThreadLocal 綁定在當前線程。


  • 而事務信息,就包括事務對應的 Connection 連接。那也就意味著,還沒走到 OrderMapper 的查詢操作,Connection 就已經被創建出來了。并且,因為事務信息會和當前線程綁定在一起,在 OrderMapper 在查詢操作需要獲得 Connection 時,就直接拿到當前線程綁定的 Connection ,而不是 OrderMapper 添加 @DS 注解所對應的 DataSource 所對應的 Connection 。

  • OK ,那么我們現在可以把問題聚焦到 DataSourceTransactionManager 是怎么獲取 DataSource 從而獲得 Connection 的了。對于每個 DataSourceTransactionManager 數據庫事務管理器,創建時都會傳入其需要管理的 DataSource 數據源。在使用 dynamic-datasource-spring-boot-starter 時,它創建了一個 DynamicRoutingDataSource ,傳入到 DataSourceTransactionManager 中。

  • 而 DynamicRoutingDataSource 負責管理我們配置的多個數據源。例如說,本示例中就管理了 a、b、c 三個數據源,并且默認使用 a 數據源。那么在當前場景下,DynamicRoutingDataSource 需要基于 @DS 獲得數據源名,從而獲得對應的 DataSource ,結果因為我們在 Service 方法上,并沒有添加 @DS 注解,所以它只好返回默認數據源,也就是 a 。故此,就發生了 找不到表 的異常。

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

解決方法

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

pps:

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

動態數據源切換失敗

由事務@Transactional注解導致動態數據源切換失效的問題

不多BB,直接上代碼:

public class DataSourceKey {
    /**
     * 用戶數據源
     */
    public final static String USER = "userDataSource";
    /**
     * 報表數據源
     */
    public final static String REPORT = "reportDataSource";
    /**
     * 所有數據源的集合
     */
    final static List<String> SOURCES = ImmutableList.of(USER, REPORT);
    /**
     * 根據包名找到數據源, 多數據源的前綴不能存在相同的。 例: user -> userDataSource
     *
     * @param pack 包名
     * @return 數據源名
     */
    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("切換數據源開始。。。。。。。。。。。。");
        Package pack = point.getSignature().getDeclaringType().getPackage();
        String str = StringUtils.substringAfterLast(pack.getName(), ".");
        String dataSourceKey = DataSourceKey.getDataSourceKey(str);
        DynamicDataSourceHolder.set(dataSourceKey);
        log.debug("切換數據源成功,當前數據源:{}", dataSourceKey);
    }
    @After("dataSourcePointcut()")
    public void doAfterReturning() throws Throwable {
        DynamicDataSourceHolder.clear();
    }
}
/**
 * 動態數據源持有者
 */
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();
    }
}
/**
 * 動態數據源配置
 */
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceHolder.get();
    }
}
//正確的代碼   --這里偷懶了,直接controller調用dao層
@GetMapping("/testdb")
    public String testDateSources(){
     //縮小事務的范圍
       add();
       rptFieldMapper.selectxxxx();
       
       return "sss";
    }
    @Transactional(rollbackFor = Exception.class)
    public void add() {
     userMapper.insertXXXX(xxxx);
 }
//錯誤的代碼,@Transactional注解會導致 數據源切換失敗
    @GetMapping("/testdb")
    @Transactional(rollbackFor = Exception.class)
    public String testDateSources(){  
        userMapper.insertXXXX(xxxx);
        rptFieldMapper.selectXXXX();
        
        return "sss";
    }

“java中@Transactional跟@DS動態數據源注解沖突問題怎么解決”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

泌阳县| 抚远县| 基隆市| 襄垣县| 通城县| 博客| 尼勒克县| 乐至县| 海阳市| 定兴县| 措美县| 澄迈县| 白河县| 衡阳市| 金坛市| 兴隆县| 新兴县| 新巴尔虎左旗| 怀仁县| 九寨沟县| 石楼县| 通许县| 兴宁市| 和田市| 武义县| 石首市| 宣汉县| 安新县| 平泉县| 根河市| 清苑县| 五大连池市| 义马市| 祁门县| 上饶市| 郎溪县| 闸北区| 临漳县| 武清区| 彭阳县| 禹城市|