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

This commit is contained in:
zhou-hao
2019-03-29 22:53:42 +08:00
parent 667a8020af
commit cac042ba18
8 changed files with 68 additions and 32 deletions

View File

@@ -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
```
编程方式:

View File

@@ -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();
} else {
throw new DataSourceNotFoundException(id);
if (StringUtils.hasText(id)) {
if (id.contains("${")) {
id = ExpressionUtils.analytical(id, context.getParams(), "spel");
}
} else {
DataSourceHolder.switcher().use(id);
if (!DataSourceHolder.existing(id)) {
if (strategy.isFallbackDefault()) {
DataSourceHolder.switcher().useDefault();
} else {
throw new DataSourceNotFoundException("数据源[" + 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();
}
});

View File

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

View File

@@ -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;

View File

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

View File

@@ -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;

View File

@@ -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容器即可

View File

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