Przeglądaj źródła

优化数据源,增加动态切换数据库

zhou-hao 6 lat temu
rodzic
commit
cac042ba18

+ 3 - 1
hsweb-datasource/README.md

@@ -15,8 +15,10 @@ hsweb:
            test: # 只是一个标识
               # 拦截类和方法的表达式
               expression: org.hswebframework.**.*Service.find*
-              # 使用数据源
+              # 切换数据源
               data-source-id: read_db
+              # 切换数据库 从3.0.8开始支持
+              #database: db_001  # select * from db_001.s_user
 ```
 
 编程方式:

+ 27 - 14
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/AopDataSourceSwitcherAutoConfiguration.java

@@ -13,6 +13,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.util.ClassUtils;
+import org.springframework.util.StringUtils;
 
 import java.lang.reflect.Method;
 import java.util.List;
@@ -67,8 +68,8 @@ public class AopDataSourceSwitcherAutoConfiguration {
     }
 
     public static class SwitcherMethodMatcherPointcutAdvisor extends StaticMethodMatcherPointcutAdvisor {
-        private static final Logger logger           = LoggerFactory.getLogger(SwitcherMethodMatcherPointcutAdvisor.class);
-        private static final long   serialVersionUID = 536295121851990398L;
+        private static final Logger logger = LoggerFactory.getLogger(SwitcherMethodMatcherPointcutAdvisor.class);
+        private static final long serialVersionUID = 536295121851990398L;
 
         private List<DataSourceSwitchStrategyMatcher> matchers;
 
@@ -76,7 +77,7 @@ public class AopDataSourceSwitcherAutoConfiguration {
 
         private Map<CachedDataSourceSwitchStrategyMatcher.CacheKey, DataSourceSwitchStrategyMatcher> cache
                 = new ConcurrentHashMap<>();
-        private Map<CachedTableSwitchStrategyMatcher.CacheKey, TableSwitchStrategyMatcher>           tableCache
+        private Map<CachedTableSwitchStrategyMatcher.CacheKey, TableSwitchStrategyMatcher> tableCache
                 = new ConcurrentHashMap<>();
 
         public SwitcherMethodMatcherPointcutAdvisor(List<DataSourceSwitchStrategyMatcher> matchers,
@@ -92,7 +93,9 @@ public class AopDataSourceSwitcherAutoConfiguration {
 
                 Consumer<MethodInterceptorContext> before = context -> {
                 };
-                AtomicBoolean dataSourceChanged = new AtomicBoolean(true);
+                AtomicBoolean dataSourceChanged = new AtomicBoolean(false);
+                AtomicBoolean databaseChanged = new AtomicBoolean(false);
+
                 if (matcher != null) {
                     before = before.andThen(context -> {
                         Strategy strategy = matcher.getStrategy(context);
@@ -100,23 +103,26 @@ public class AopDataSourceSwitcherAutoConfiguration {
                             dataSourceChanged.set(false);
                             logger.warn("strategy matcher found:{}, but strategy is null!", matcher);
                         } else {
-                            logger.debug("switch datasource.use strategy:{}", strategy);
+                            logger.debug("switch datasource. use strategy:{}", strategy);
                             if (strategy.isUseDefaultDataSource()) {
                                 DataSourceHolder.switcher().useDefault();
                             } else {
                                 try {
                                     String id = strategy.getDataSourceId();
-                                    if (id.contains("${")) {
-                                        id = ExpressionUtils.analytical(id, context.getParams(), "spel");
-                                    }
-                                    if (!DataSourceHolder.existing(id)) {
-                                        if (strategy.isFallbackDefault()) {
-                                            DataSourceHolder.switcher().useDefault();
+                                    if (StringUtils.hasText(id)) {
+                                        if (id.contains("${")) {
+                                            id = ExpressionUtils.analytical(id, context.getParams(), "spel");
+                                        }
+                                        if (!DataSourceHolder.existing(id)) {
+                                            if (strategy.isFallbackDefault()) {
+                                                DataSourceHolder.switcher().useDefault();
+                                            } else {
+                                                throw new DataSourceNotFoundException("数据源[" + id + "]不存在");
+                                            }
                                         } else {
-                                            throw new DataSourceNotFoundException(id);
+                                            DataSourceHolder.switcher().use(id);
                                         }
-                                    } else {
-                                        DataSourceHolder.switcher().use(id);
+                                        dataSourceChanged.set(true);
                                     }
                                 } catch (RuntimeException e) {
                                     dataSourceChanged.set(false);
@@ -126,6 +132,10 @@ public class AopDataSourceSwitcherAutoConfiguration {
                                     throw new RuntimeException(e.getMessage(), e);
                                 }
                             }
+                            if (StringUtils.hasText(strategy.getDatabase())) {
+                                databaseChanged.set(true);
+                                DataSourceHolder.databaseSwitcher().use(strategy.getDatabase());
+                            }
                         }
                     });
                 }
@@ -149,6 +159,9 @@ public class AopDataSourceSwitcherAutoConfiguration {
                     if (dataSourceChanged.get()) {
                         DataSourceHolder.switcher().useLast();
                     }
+                    if (databaseChanged.get()) {
+                        DataSourceHolder.databaseSwitcher().useLast();
+                    }
                     DataSourceHolder.tableSwitcher().reset();
                 }
             });

+ 8 - 1
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/annotation/UseDataSource.java

@@ -17,7 +17,14 @@ public @interface UseDataSource {
      * @return 数据源ID ,支持表达式如 : ${#param.id}
      * @see DynamicDataSource#getId()
      */
-    String value();
+    String value() default "";
+
+    /**
+     * 指定数据库
+     *
+     * @return 数据库名
+     */
+    String database() default "";
 
     /**
      * @return 数据源不存在时, 是否使用默认数据源.

+ 6 - 0
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/strategy/AnnotationDataSourceSwitchStrategyMatcher.java

@@ -3,6 +3,7 @@ package org.hswebframework.web.datasource.strategy;
 import org.hswebframework.web.AopUtils;
 import org.hswebframework.web.datasource.annotation.UseDataSource;
 import org.hswebframework.web.datasource.annotation.UseDefaultDataSource;
+import org.springframework.util.StringUtils;
 
 import java.lang.reflect.Method;
 import java.util.Arrays;
@@ -46,6 +47,11 @@ public class AnnotationDataSourceSwitchStrategyMatcher extends CachedDataSourceS
                 public String toString() {
                     return "Annotation Strategy(" + (useDataSource != null ? useDataSource : useDefaultDataSource) + ")";
                 }
+
+                @Override
+                public String getDatabase() {
+                    return useDataSource == null ? null : StringUtils.isEmpty(useDataSource.database()) ? null : useDataSource.database();
+                }
             };
         }
         return null;

+ 6 - 0
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/strategy/DataSourceSwitchStrategyMatcher.java

@@ -55,6 +55,12 @@ public interface DataSourceSwitchStrategyMatcher {
          * @see org.hswebframework.web.datasource.switcher.DataSourceSwitcher#use(String)
          */
         String getDataSourceId();
+
+        /**
+         * @since 3.0.8
+         * @return 指定数据库
+         */
+        String getDatabase();
     }
 
 }

+ 7 - 6
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/strategy/ExpressionDataSourceSwitchStrategyMatcher.java

@@ -17,10 +17,10 @@ import java.util.Map;
  *      datasource:
  *          switcher:
  *              test: # 只是一个标识
- *              # 拦截类和方法的表达式
- *              expression: org.hswebframework.**.*Service.find*
- *              # 使用数据源
- *              data-source-id: read_db
+ *                  # 拦截类和方法的表达式
+ *                  expression: org.hswebframework.**.*Service.find*
+ *                  # 使用数据源
+ *                  data-source-id: read_db
  * </pre>
  *
  * @author zhouhao
@@ -53,8 +53,9 @@ public class ExpressionDataSourceSwitchStrategyMatcher extends CachedDataSourceS
     @Setter
     public static class ExpressionStrategy implements Strategy {
         private boolean useDefaultDataSource = false;
-        private boolean fallbackDefault      = false;
-        private String  dataSourceId         = null;
+        private boolean fallbackDefault = false;
+        private String dataSourceId = null;
+        private String database;
         private String expression;
         private String id;
 

+ 9 - 8
hsweb-datasource/hsweb-datasource-jta/README.md

@@ -25,7 +25,8 @@ spring:
 ```yaml
 hsweb:
   datasource:
-    test_ds:  # 数据源ID
+    jta:
+      test_ds:  # 数据源ID
         xa-data-source-class-name: com.alibaba.druid.pool.xa.DruidXADataSource
         xa-properties: # 数据源的配置属性
           url: jdbc:h2:mem:test;DB_CLOSE_ON_EXIT=FALSE
@@ -33,16 +34,16 @@ hsweb:
           password:
         max-pool-size: 20
         borrow-connection-timeout: 1000
-    test_ds2: # 数据源ID
-      xa-data-source-class-name: com.alibaba.druid.pool.xa.DruidXADataSource
-      xa-properties: # 数据源的配置属性
+      test_ds2: # 数据源ID
+        xa-data-source-class-name: com.alibaba.druid.pool.xa.DruidXADataSource
+        xa-properties: # 数据源的配置属性
         url: jdbc:mysql://localhost:3306/hsweb?pinGlobalTxToPhysicalConnection=true&useSSL=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false
-#            url: jdbc:h2:mem:test2;DB_CLOSE_ON_EXIT=FALSE
+        #            url: jdbc:h2:mem:test2;DB_CLOSE_ON_EXIT=FALSE
         username: root
         password: "123456" # 纯数字密码要加上双引号,不然启动会报Cannot initialize AtomikosDataSourceBean
-      max-pool-size: 20
-      borrow-connection-timeout: 1000
-      init-timeout: 20
+        max-pool-size: 20
+        borrow-connection-timeout: 1000
+        init-timeout: 20
 ```
 
 自定义,将数据源配置放到数据库中,实现 ``DynamicDataSourceConfigRepository<AtomikosDataSourceConfig>`` 接口并注入到spring容器即可

+ 1 - 1
hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/AtomikosDataSourceAutoConfiguration.java

@@ -27,7 +27,7 @@ public class AtomikosDataSourceAutoConfiguration {
         return new AtomikosDataSourceBean();
     }
 
-    @ConditionalOnMissingBean(JtaDataSourceRepository.class)
+    @ConditionalOnMissingBean(DynamicDataSourceConfigRepository.class)
     @Bean
     public InMemoryAtomikosDataSourceRepository memoryJtaDataSourceStore() {
         return new InMemoryAtomikosDataSourceRepository();