feat: 添加数据源缺失处理器

This commit is contained in:
oc 2025-02-19 12:09:20 +08:00
parent 5fccfa6c4b
commit a39fb3d014
4 changed files with 140 additions and 7 deletions

View File

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

View File

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

View File

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

View File

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