mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-06 16:48:24 +08:00
feat: 添加数据源缺失处理器
This commit is contained in:
parent
5fccfa6c4b
commit
a39fb3d014
@ -206,6 +206,43 @@ public class Account {
|
||||
`DataSourceKey.use()` > `@UseDataSource()在方法上` > `@UseDataSource()在类上` >`@Table(dataSource="...")`
|
||||
:::
|
||||
|
||||
## 数据源缺失处理器
|
||||
|
||||
当无法根据 `dataSourceKey` 找到数据源时,默认情况下会抛出 `IllegalStateException` 异常。
|
||||
数据源缺失处理器(`DataSourceMissingHandler`)提供了更加灵活的处理方式,你可以通过它自定义处理逻辑(如:记录日志、抛出异常或主动初始化新的数据源)。
|
||||
|
||||
### 使用示例
|
||||
|
||||
我们推荐使用 `MyBatisFlexCustomizer` 来配置数据源缺失处理器,如下所示:
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class MyBatisFlexConfiguration implements MyBatisFlexCustomizer {
|
||||
|
||||
@Override
|
||||
public void customize(FlexGlobalConfig globalConfig) {
|
||||
// ...
|
||||
|
||||
// 配置数据源缺失处理器:此处演示的是以后备逻辑主动初始化数据源
|
||||
globalConfig.setDataSourceMissingHandler((dataSourceKey, dataSourceMap) -> {
|
||||
// 根据 key 获取数据源
|
||||
DataSource ds = customCreateDataSource(dataSourceKey);
|
||||
|
||||
// 取不到的时候返回 null,后续代码逻辑仍然由 FlexDataSource 处理(即抛出异常)
|
||||
if (ds == null) return null;
|
||||
|
||||
// 添加新的数据源,避免下次再次触发和创建
|
||||
dataSourceMap.put(dataSourceKey, ds);
|
||||
|
||||
return dataSourceMap;
|
||||
});
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## 更多的 Spring 或 Solon Yaml 配置支持
|
||||
```yaml
|
||||
mybatis-flex:
|
||||
|
||||
@ -20,6 +20,7 @@ import com.mybatisflex.annotation.KeyType;
|
||||
import com.mybatisflex.annotation.Listener;
|
||||
import com.mybatisflex.annotation.SetListener;
|
||||
import com.mybatisflex.annotation.UpdateListener;
|
||||
import com.mybatisflex.core.datasource.DataSourceMissingHandler;
|
||||
import com.mybatisflex.core.datasource.FlexDataSource;
|
||||
import com.mybatisflex.core.dialect.DbType;
|
||||
import com.mybatisflex.core.exception.FlexAssert;
|
||||
@ -115,6 +116,11 @@ public class FlexGlobalConfig {
|
||||
*/
|
||||
private UnMappedColumnHandler unMappedColumnHandler;
|
||||
|
||||
/**
|
||||
* 数据源缺失处理器
|
||||
*/
|
||||
private DataSourceMissingHandler dataSourceMissingHandler;
|
||||
|
||||
public boolean isPrintBanner() {
|
||||
return printBanner;
|
||||
}
|
||||
@ -349,6 +355,21 @@ public class FlexGlobalConfig {
|
||||
FlexGlobalConfig.globalConfigs = globalConfigs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据源缺失处理器。
|
||||
* @return DataSourceMissingHandler 数据源缺失处理器实例,用于自定义处理逻辑(如:记录日志、抛出异常或提供默认数据源)。
|
||||
*/
|
||||
public DataSourceMissingHandler getDataSourceMissingHandler() {
|
||||
return dataSourceMissingHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置获取数据源缺失处理器。
|
||||
* @param dataSourceMissingHandler 数据源缺失处理器实例,用于自定义处理逻辑(如:记录日志、抛出异常或提供默认数据源)。
|
||||
*/
|
||||
public void setDataSourceMissingHandler(final DataSourceMissingHandler dataSourceMissingHandler) {
|
||||
this.dataSourceMissingHandler = dataSourceMissingHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对应的是 注解 {@link com.mybatisflex.annotation.Id} 的配置
|
||||
@ -386,7 +407,7 @@ public class FlexGlobalConfig {
|
||||
}
|
||||
|
||||
|
||||
/////static factory methods/////
|
||||
/// //static factory methods/////
|
||||
private static ConcurrentHashMap<String, FlexGlobalConfig> globalConfigs = new ConcurrentHashMap<>();
|
||||
private static FlexGlobalConfig defaultConfig = new FlexGlobalConfig();
|
||||
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
package com.mybatisflex.core.datasource;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 数据源缺失处理器接口。当尝试获取指定数据源但不存在时,通过此接口进行动态处理。
|
||||
*
|
||||
* <p>
|
||||
* 该接口被设计为函数式接口,可通过Lambda表达式或方法引用实现,用于在运行时动态处理缺失的数据源。<br/>
|
||||
* 常见应用场景:<br/>
|
||||
* - 多租户系统中根据租户ID动态创建并缓存数据源;<br/>
|
||||
* - 数据源缺失时的主动初始化;
|
||||
* </p>
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DataSourceMissingHandler {
|
||||
/**
|
||||
* 处理缺失数据源的核心方法。
|
||||
*
|
||||
* @param dataSourceKey 当前请求的数据源键(标识符),通常用于识别目标数据源
|
||||
* @param dataSourceMap 当前已存在的数据源集合(key: 数据源键,value: 数据源实例)
|
||||
* @return 处理后的新数据源集合,通常应包含原有数据源及新增处理的数据源
|
||||
* @implSpec 实现类应通过此方法实现:<br/>
|
||||
* 1. 根据dataSourceKey识别需要补充的数据源<br/>
|
||||
* 2. 创建/配置新的DataSource实例<br/>
|
||||
* 3. 将新数据源添加到dataSourceMap中<br/>
|
||||
* 4. 返回更新后的数据源集合<br/>
|
||||
* 5. 如返回 null 或 Map 为空,后续会抛出异常。<br/>
|
||||
* @example 示例场景:
|
||||
* 当请求"tenant_123"数据源不存在时,在此方法中创建对应数据源并放入返回的Map
|
||||
*/
|
||||
Map<String, DataSource> handle(String dataSourceKey, Map<String, DataSource> dataSourceMap);
|
||||
}
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.mybatisflex.core.datasource;
|
||||
|
||||
import com.mybatisflex.core.FlexGlobalConfig;
|
||||
import com.mybatisflex.core.dialect.DbType;
|
||||
import com.mybatisflex.core.dialect.DbTypeUtil;
|
||||
import com.mybatisflex.core.transaction.TransactionContext;
|
||||
@ -30,7 +31,11 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
@ -182,7 +187,7 @@ public class FlexDataSource extends AbstractDataSource {
|
||||
} catch (SQLException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Error resetting autoCommit to true before closing the connection. " +
|
||||
"Cause: " + e);
|
||||
"Cause: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -217,38 +222,74 @@ public class FlexDataSource extends AbstractDataSource {
|
||||
return (iface.isInstance(this) || getDataSource().isWrapperFor(iface));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据源缺失处理器。
|
||||
* @return DataSourceMissingHandler 数据源缺失处理器实例,用于自定义处理逻辑(如:记录日志、抛出异常或提供默认数据源)。
|
||||
*/
|
||||
public DataSourceMissingHandler getDataSourceMissingHandler() {
|
||||
return FlexGlobalConfig.getDefaultConfig().getDataSourceMissingHandler();
|
||||
}
|
||||
|
||||
protected DataSource getDataSource() {
|
||||
DataSource dataSource = defaultDataSource;
|
||||
DataSourceMissingHandler dataSourceMissingHandler = getDataSourceMissingHandler();
|
||||
|
||||
if (dataSourceMap.size() > 1) {
|
||||
String dataSourceKey = DataSourceKey.get();
|
||||
|
||||
if (StringUtil.hasText(dataSourceKey)) {
|
||||
//负载均衡 key
|
||||
// 负载均衡 key
|
||||
if (dataSourceKey.charAt(dataSourceKey.length() - 1) == LOAD_BALANCE_KEY_SUFFIX) {
|
||||
String prefix = dataSourceKey.substring(0, dataSourceKey.length() - 1);
|
||||
List<String> matchedKeys = new ArrayList<>();
|
||||
|
||||
for (String key : dataSourceMap.keySet()) {
|
||||
if (key.startsWith(prefix)) {
|
||||
matchedKeys.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
// 当找不到匹配的 key 时,尝试后备匹配
|
||||
if (matchedKeys.isEmpty() && dataSourceMissingHandler != null) {
|
||||
Map<String, DataSource> dsMap = dataSourceMissingHandler.handle(dataSourceKey, dataSourceMap);
|
||||
|
||||
if (dsMap != null && !dsMap.isEmpty()) {
|
||||
for (String key : dsMap.keySet()) {
|
||||
if (key.startsWith(prefix)) {
|
||||
matchedKeys.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matchedKeys.isEmpty()) {
|
||||
throw new IllegalStateException("Can not matched dataSource by key: \"" + dataSourceKey + "\"");
|
||||
}
|
||||
|
||||
String randomKey = matchedKeys.get(ThreadLocalRandom.current().nextInt(matchedKeys.size()));
|
||||
|
||||
return dataSourceMap.get(randomKey);
|
||||
}
|
||||
//非负载均衡 key
|
||||
// 非负载均衡 key
|
||||
else {
|
||||
dataSource = dataSourceMap.get(dataSourceKey);
|
||||
|
||||
// 当找不到匹配的 key 时,尝试后备匹配
|
||||
if (dataSource == null && dataSourceMissingHandler != null) {
|
||||
Map<String, DataSource> dsMap = dataSourceMissingHandler.handle(dataSourceKey, dataSourceMap);
|
||||
|
||||
if (dsMap != null && !dsMap.isEmpty()) {
|
||||
dataSource = dsMap.get(dataSourceKey);
|
||||
}
|
||||
}
|
||||
|
||||
if (dataSource == null) {
|
||||
throw new IllegalStateException("Cannot get target dataSource by key: \"" + dataSourceKey + "\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@ -267,11 +308,11 @@ public class FlexDataSource extends AbstractDataSource {
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
if (ArrayUtil.contains(proxyMethods, method.getName())
|
||||
&& isTransactional()) {
|
||||
//do nothing
|
||||
// do nothing
|
||||
return null;
|
||||
}
|
||||
|
||||
//setAutoCommit: true
|
||||
// setAutoCommit: true
|
||||
if ("close".equalsIgnoreCase(method.getName())) {
|
||||
resetAutoCommit(original);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user