1. 程式人生 > >spring boot 註解方式配置多資料來源與使用

spring boot 註解方式配置多資料來源與使用

1、首先看一下application-dev.yml 配置

spring:

    datasource:

        type: com.alibaba.druid.pool.DruidDataSource

        druid:

            first:  #資料來源1

                driverClassName: com.mysql.jdbc.Driver

                url: jdbc:mysql://127.0.0.1:3306/xujl?useUnicode=true&characterEncoding=UTF-8

               username: root

                password: admin

            second:  #資料來源2

                url: jdbc:postgresql://172.xx.x.xx:xxxx/pcsu?charSet=utf-8

                username: suit_test

                password: suit_test

                driverClassName: org.postgresql.Driver

            initial-size: 10

            max-active: 100

            min-idle: 10

           max-wait: 60000

            pool-prepared-statements: true

            max-pool-prepared-statement-per-connection-size: 20

            time-between-eviction-runs-millis: 60000

            min-evictable-idle-time-millis: 300000

            validation-query: SELECT 1 FROM DUAL

            test-while-idle: true

            test-on-borrow: false

            test-on-return: false

            stat-view-servlet:

                enabled: true

                url-pattern: /druid/*

                #login-username: admin

                #login-password: admin

            filter:

                stat:

                    log-slow-sql: true

                    slow-sql-millis: 1000

                    merge-sql: true

                wall:

                    config:

                       multi-statement-allow: true

上面的方式實現就已經配置了兩個 資料來源了,下面來看下程式碼的實現

2、配置一個註解,方便使用,直接在需要配置的方法上面加上資料來源即可

@Target({ ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface DataSource {

    String name() default "";

}

2-1:配置管理多資料來源的名稱,方便管理。

  1. /**
  2.  * 增加多資料來源,在此配置
  3.  */
  4. public interface DataSourceNames {
  5.     String FIRST = "first";
  6.     String SECOND = "second";
  7. }

3、動態資料來源載入

  1. /**
  2.  * 動態資料來源
  3.  */
  4. public class DynamicDataSource extends AbstractRoutingDataSource {
  5.     //用來儲存資料來源與獲取資料來源
  6.     private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
  7.  
  8.     public DynamicDataSource(DataSource defaultTargetDataSource, Map<String, DataSource> targetDataSources) {
  9.         super.setDefaultTargetDataSource(defaultTargetDataSource);
  10.         super.setTargetDataSources(new HashMap<Object, Object>(targetDataSources));
  11.         super.afterPropertiesSet();
  12.     }
  13.  
  14.     @Override
  15.     protected Object determineCurrentLookupKey() {
  16.         return getDataSource();
  17.     }
  18.  
  19.     public static void setDataSource(String dataSource) {
  20.         contextHolder.set(dataSource);
  21.     }
  22.  
  23.     public static String getDataSource() {
  24.         return contextHolder.get();
  25.     }
  26.  
  27.     public static void clearDataSource() {
  28.         contextHolder.remove();
  29.     }
  30.  
  31. }

這裡有必要說一下AbstractRoutingDataSource這個類,載入一個圖片:

可以看到AbstractRoutingDataSource獲取資料來源之前會先呼叫determineCurrentLookupKey方法查詢當前的lookupKey,這個lookupKey就是資料來源標識。

因此通過重寫這個查詢資料來源標識的方法就可以讓spring切換到指定的資料來源了。

(這張圖片出自:點選開啟連結 尊重原創。)

3-1:重要一點:吧上面的信心載入到配置中

  1. /**
  2.  * 配置多資料來源
  3.  */
  4. @Configuration
  5. public class DynamicDataSourceConfig {
  6.  
  7.     @Bean
  8.     @ConfigurationProperties("spring.datasource.druid.first")
  9.     public DataSource firstDataSource(){
  10.         return DruidDataSourceBuilder.create().build();
  11.     }
  12.  
  13.     @Bean
  14.     @ConfigurationProperties("spring.datasource.druid.second")
  15.     public DataSource secondDataSource(){
  16.         return DruidDataSourceBuilder.create().build();
  17.     }
  18.  
  19.     @Bean
  20.     @Primary
  21.     public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource) {
  22.         Map<String, DataSource> targetDataSources = new HashMap<>();
  23.         targetDataSources.put(DataSourceNames.FIRST, firstDataSource);
  24.         targetDataSources.put(DataSourceNames.SECOND, secondDataSource);
  25.         return new DynamicDataSource(firstDataSource, targetDataSources);
  26.     }
  27. }

4、最最重要的一步,就是使用spring的aop原理,切面方式載入資料來源

  1. /**
  2.  * 多資料來源,切面處理類 處理帶有註解的方法類
  3.  */
  4. @Aspect
  5. @Component
  6. public class DataSourceAspect implements Ordered {
  7.  
  8. protected Logger logger = LoggerFactory.getLogger(getClass());
  9.  
  10. @Pointcut("@annotation(xxxx.DataSource)")//注意:這裡的xxxx代表的是上面public @interface DataSource這個註解DataSource的包名
  11. public void dataSourcePointCut() {
  12.  
  13. }
  14.  
  15. @Around("dataSourcePointCut()")
  16. public Object around(ProceedingJoinPoint point) throws Throwable {
  17. MethodSignature signature = (MethodSignature) point.getSignature();
  18. Method method = signature.getMethod();
  19. DataSource ds = method.getAnnotation(DataSource.class);
  20. if (ds == null) {
  21. DynamicDataSource.setDataSource(DataSourceNames.FIRST);
  22. logger.debug("set datasource is " + DataSourceNames.FIRST);
  23.            } else {
  24. DynamicDataSource.setDataSource(ds.name());
  25. logger.debug("set datasource is " + ds.name());
  26. }
  27. try {
  28. return point.proceed();
  29.            } finally {
  30. DynamicDataSource.clearDataSource();
  31. logger.debug("clean datasource");
  32. }
  33. }
  34.  
  35.  
  36.  
  37. @Override
  38. public int getOrder() {
  39. return 1;
  40. }
  41. }

5、最後一步就是使用了在你的service的實現類 serviceImpl上面進行註解,這裡是重點(我剛開一直放在dao上面,因為我用的是mybatis,以為就是要放在這個上面,結果一直出不來,最後才知道應該放在serviceImpl上面)@Override

  1. @DataSource(name="second")
  2. public List<IntegralExchangeRule> list(Map<String,Object> map) {
  3. return dao.list(map);
  4. }

OK,現在已經全部配置完成,可以使用了

有必要說一下:我上面的DataSourceAspect這個類裡面around方法裡面,已經預設是資料來源1,如果你不配置@DaeSource(name=""),它預設會使用第一個資料來源,否則的話,按照你的資料來源名稱去使用的。;

 

**歡迎關注我的個人公眾號:we-aibook,裡面有相關技術文章分享,專案架構,知識星球,技術交流群,不定期進行抽獎送書活動喲!**