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.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 配置支持
|
## 更多的 Spring 或 Solon Yaml 配置支持
|
||||||
```yaml
|
```yaml
|
||||||
mybatis-flex:
|
mybatis-flex:
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import com.mybatisflex.annotation.KeyType;
|
|||||||
import com.mybatisflex.annotation.Listener;
|
import com.mybatisflex.annotation.Listener;
|
||||||
import com.mybatisflex.annotation.SetListener;
|
import com.mybatisflex.annotation.SetListener;
|
||||||
import com.mybatisflex.annotation.UpdateListener;
|
import com.mybatisflex.annotation.UpdateListener;
|
||||||
|
import com.mybatisflex.core.datasource.DataSourceMissingHandler;
|
||||||
import com.mybatisflex.core.datasource.FlexDataSource;
|
import com.mybatisflex.core.datasource.FlexDataSource;
|
||||||
import com.mybatisflex.core.dialect.DbType;
|
import com.mybatisflex.core.dialect.DbType;
|
||||||
import com.mybatisflex.core.exception.FlexAssert;
|
import com.mybatisflex.core.exception.FlexAssert;
|
||||||
@ -115,6 +116,11 @@ public class FlexGlobalConfig {
|
|||||||
*/
|
*/
|
||||||
private UnMappedColumnHandler unMappedColumnHandler;
|
private UnMappedColumnHandler unMappedColumnHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据源缺失处理器
|
||||||
|
*/
|
||||||
|
private DataSourceMissingHandler dataSourceMissingHandler;
|
||||||
|
|
||||||
public boolean isPrintBanner() {
|
public boolean isPrintBanner() {
|
||||||
return printBanner;
|
return printBanner;
|
||||||
}
|
}
|
||||||
@ -349,6 +355,21 @@ public class FlexGlobalConfig {
|
|||||||
FlexGlobalConfig.globalConfigs = globalConfigs;
|
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} 的配置
|
* 对应的是 注解 {@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 ConcurrentHashMap<String, FlexGlobalConfig> globalConfigs = new ConcurrentHashMap<>();
|
||||||
private static FlexGlobalConfig defaultConfig = new FlexGlobalConfig();
|
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;
|
package com.mybatisflex.core.datasource;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.FlexGlobalConfig;
|
||||||
import com.mybatisflex.core.dialect.DbType;
|
import com.mybatisflex.core.dialect.DbType;
|
||||||
import com.mybatisflex.core.dialect.DbTypeUtil;
|
import com.mybatisflex.core.dialect.DbTypeUtil;
|
||||||
import com.mybatisflex.core.transaction.TransactionContext;
|
import com.mybatisflex.core.transaction.TransactionContext;
|
||||||
@ -30,7 +31,11 @@ import java.lang.reflect.Method;
|
|||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
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;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -182,7 +187,7 @@ public class FlexDataSource extends AbstractDataSource {
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
log.debug("Error resetting autoCommit to true before closing the connection. " +
|
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 (iface.isInstance(this) || getDataSource().isWrapperFor(iface));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取数据源缺失处理器。
|
||||||
|
* @return DataSourceMissingHandler 数据源缺失处理器实例,用于自定义处理逻辑(如:记录日志、抛出异常或提供默认数据源)。
|
||||||
|
*/
|
||||||
|
public DataSourceMissingHandler getDataSourceMissingHandler() {
|
||||||
|
return FlexGlobalConfig.getDefaultConfig().getDataSourceMissingHandler();
|
||||||
|
}
|
||||||
|
|
||||||
protected DataSource getDataSource() {
|
protected DataSource getDataSource() {
|
||||||
DataSource dataSource = defaultDataSource;
|
DataSource dataSource = defaultDataSource;
|
||||||
|
DataSourceMissingHandler dataSourceMissingHandler = getDataSourceMissingHandler();
|
||||||
|
|
||||||
if (dataSourceMap.size() > 1) {
|
if (dataSourceMap.size() > 1) {
|
||||||
String dataSourceKey = DataSourceKey.get();
|
String dataSourceKey = DataSourceKey.get();
|
||||||
|
|
||||||
if (StringUtil.hasText(dataSourceKey)) {
|
if (StringUtil.hasText(dataSourceKey)) {
|
||||||
//负载均衡 key
|
// 负载均衡 key
|
||||||
if (dataSourceKey.charAt(dataSourceKey.length() - 1) == LOAD_BALANCE_KEY_SUFFIX) {
|
if (dataSourceKey.charAt(dataSourceKey.length() - 1) == LOAD_BALANCE_KEY_SUFFIX) {
|
||||||
String prefix = dataSourceKey.substring(0, dataSourceKey.length() - 1);
|
String prefix = dataSourceKey.substring(0, dataSourceKey.length() - 1);
|
||||||
List<String> matchedKeys = new ArrayList<>();
|
List<String> matchedKeys = new ArrayList<>();
|
||||||
|
|
||||||
for (String key : dataSourceMap.keySet()) {
|
for (String key : dataSourceMap.keySet()) {
|
||||||
if (key.startsWith(prefix)) {
|
if (key.startsWith(prefix)) {
|
||||||
matchedKeys.add(key);
|
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()) {
|
if (matchedKeys.isEmpty()) {
|
||||||
throw new IllegalStateException("Can not matched dataSource by key: \"" + dataSourceKey + "\"");
|
throw new IllegalStateException("Can not matched dataSource by key: \"" + dataSourceKey + "\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
String randomKey = matchedKeys.get(ThreadLocalRandom.current().nextInt(matchedKeys.size()));
|
String randomKey = matchedKeys.get(ThreadLocalRandom.current().nextInt(matchedKeys.size()));
|
||||||
|
|
||||||
return dataSourceMap.get(randomKey);
|
return dataSourceMap.get(randomKey);
|
||||||
}
|
}
|
||||||
//非负载均衡 key
|
// 非负载均衡 key
|
||||||
else {
|
else {
|
||||||
dataSource = dataSourceMap.get(dataSourceKey);
|
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) {
|
if (dataSource == null) {
|
||||||
throw new IllegalStateException("Cannot get target dataSource by key: \"" + dataSourceKey + "\"");
|
throw new IllegalStateException("Cannot get target dataSource by key: \"" + dataSourceKey + "\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dataSource;
|
return dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,11 +308,11 @@ public class FlexDataSource extends AbstractDataSource {
|
|||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
if (ArrayUtil.contains(proxyMethods, method.getName())
|
if (ArrayUtil.contains(proxyMethods, method.getName())
|
||||||
&& isTransactional()) {
|
&& isTransactional()) {
|
||||||
//do nothing
|
// do nothing
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
//setAutoCommit: true
|
// setAutoCommit: true
|
||||||
if ("close".equalsIgnoreCase(method.getName())) {
|
if ("close".equalsIgnoreCase(method.getName())) {
|
||||||
resetAutoCommit(original);
|
resetAutoCommit(original);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user