mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-06-06 14:15:50 +08:00
优化数据源,增加动态切换数据库
This commit is contained in:
@@ -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
|
||||
```
|
||||
|
||||
编程方式:
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,7 +17,14 @@ public @interface UseDataSource {
|
||||
* @return 数据源ID ,支持表达式如 : ${#param.id}
|
||||
* @see DynamicDataSource#getId()
|
||||
*/
|
||||
String value();
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* 指定数据库
|
||||
*
|
||||
* @return 数据库名
|
||||
*/
|
||||
String database() default "";
|
||||
|
||||
/**
|
||||
* @return 数据源不存在时, 是否使用默认数据源.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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容器即可
|
||||
|
||||
@@ -27,7 +27,7 @@ public class AtomikosDataSourceAutoConfiguration {
|
||||
return new AtomikosDataSourceBean();
|
||||
}
|
||||
|
||||
@ConditionalOnMissingBean(JtaDataSourceRepository.class)
|
||||
@ConditionalOnMissingBean(DynamicDataSourceConfigRepository.class)
|
||||
@Bean
|
||||
public InMemoryAtomikosDataSourceRepository memoryJtaDataSourceStore() {
|
||||
return new InMemoryAtomikosDataSourceRepository();
|
||||
|
||||
Reference in New Issue
Block a user