mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-06 16:48:24 +08:00
增加了@UseDataSource取值动态处理逻辑支持处理器
1:DataSourceProcessor 为处理器接口 2:DelegatingDataSourceProcessor 为DataSourceProcessor的委托类,目的增强扩大 DataSourceProcessor类或对象的结构 3:ParamIndexDataSourceProcessor 简单参数所以读取的支持处理器 4:SpelExpressionDataSourceProcessor 支持SPEL 表达式的处理器类
This commit is contained in:
parent
c1e16abc0a
commit
193ccd3855
@ -78,6 +78,12 @@ public class DataSourceKey {
|
||||
lookup = threadLocal;
|
||||
}
|
||||
|
||||
public static String processDataSourceKey(String dataSourceKey, Object mapper, Method method, Object[] arguments) {
|
||||
String dsKey = DataSourceManager.processDataSourceKey(dataSourceKey, mapper, method, arguments);
|
||||
return dsKey != null ? dsKey : dataSourceKey;
|
||||
}
|
||||
|
||||
|
||||
public static String getShardingDsKey(String dataSource, Object mapper, Method method, Object[] args) {
|
||||
String shardingDsKey = DataSourceManager.getShardingDsKey(dataSource, mapper, method, args);
|
||||
return shardingDsKey != null ? shardingDsKey : dataSource;
|
||||
@ -89,18 +95,22 @@ public class DataSourceKey {
|
||||
public static String getByManual() {
|
||||
throw new UnsupportedOperationException("使用 DataSource.get() 代替。");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static String getByAnnotation() {
|
||||
throw new UnsupportedOperationException("使用 DataSource.get() 代替。");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void useWithAnnotation(String dataSourceKey) {
|
||||
throw new UnsupportedOperationException("使用 DataSource.use(String) 代替。");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void setAnnotationKeyThreadLocal(ThreadLocal<String> annotationKeyThreadLocal) {
|
||||
throw new UnsupportedOperationException("使用 DataSource.setThreadLocal(ThreadLocal<Deque<String>>) 代替。");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void setManualKeyThreadLocal(ThreadLocal<String> manualKeyThreadLocal) {
|
||||
throw new UnsupportedOperationException("使用 DataSource.setThreadLocal(ThreadLocal<Deque<String>>) 代替。");
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.mybatisflex.core.datasource;
|
||||
|
||||
import com.mybatisflex.core.datasource.processor.DataSourceProcessor;
|
||||
import com.mybatisflex.core.exception.FlexExceptions;
|
||||
import com.mybatisflex.core.util.ClassUtil;
|
||||
import org.apache.ibatis.logging.LogFactory;
|
||||
@ -37,6 +38,19 @@ public class DataSourceManager {
|
||||
DataSourceManager.decipher = decipher;
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态数据源key取值处理
|
||||
*/
|
||||
private static DataSourceProcessor dataSourceProcessor;
|
||||
|
||||
public static DataSourceProcessor getDataSourceProcessor() {
|
||||
return dataSourceProcessor;
|
||||
}
|
||||
|
||||
public static void setDataSourceProcessor(DataSourceProcessor dataSourceProcessor) {
|
||||
DataSourceManager.dataSourceProcessor = dataSourceProcessor;
|
||||
}
|
||||
|
||||
private static DataSourceShardingStrategy dataSourceShardingStrategy;
|
||||
|
||||
public static DataSourceShardingStrategy getDataSourceShardingStrategy() {
|
||||
@ -95,6 +109,11 @@ public class DataSourceManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
static String processDataSourceKey(String dataSourceKey, Object mapper, Method method, Object[] arguments) {
|
||||
// 如果没有配置 DataSourceProcessor 实例,则不做处理,返回原始值
|
||||
return dataSourceProcessor == null ? dataSourceKey : dataSourceProcessor.process(dataSourceKey, mapper, method, arguments);
|
||||
}
|
||||
|
||||
|
||||
static String getShardingDsKey(String dataSource, Object mapper, Method method, Object[] args) {
|
||||
return dataSourceShardingStrategy != null ? dataSourceShardingStrategy.doSharding(dataSource, mapper, method, args) : null;
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
package com.mybatisflex.core.datasource.processor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 动态数据源 @UseDataSource key 解析处理器,使用时推荐注入 DelegatingDataSourceProcessor{@link DelegatingDataSourceProcessor} 类
|
||||
* 对动态数据源注解@UseDataSource 增强处理{@link com.mybatisflex.annotation.UseDataSource}
|
||||
*
|
||||
* @author Alay
|
||||
* @since 2024-12-07 15:34
|
||||
*/
|
||||
public interface DataSourceProcessor {
|
||||
|
||||
/**
|
||||
* 数据源key解析扩展
|
||||
*
|
||||
* @param dataSourceKey 注解UseDataSource的value 值,调用process时不会为null,可能会空字符串
|
||||
* @param mapper Mapper对象(代理对象)
|
||||
* @param method Mapper当前执行的方法函数
|
||||
* @param arguments Mapper当前执行的函数参数
|
||||
* @return 数据源名称(可能为null 为 null 时表示不符合当前处理器的处理,传递到下一个处理器进行处理)
|
||||
*/
|
||||
String process(String dataSourceKey, Object mapper, Method method, Object[] arguments);
|
||||
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
package com.mybatisflex.core.datasource.processor;
|
||||
|
||||
import com.mybatisflex.core.exception.FlexAssert;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DataSourceProcessor 委托扩展类,对 DataSourceProcessor 结构进行扩大和增强
|
||||
*
|
||||
* @author Alay
|
||||
* @since 2024-12-07 15:38
|
||||
*/
|
||||
public class DelegatingDataSourceProcessor implements DataSourceProcessor {
|
||||
|
||||
private final List<DataSourceProcessor> delegates;
|
||||
|
||||
|
||||
private DelegatingDataSourceProcessor(List<DataSourceProcessor> delegates) {
|
||||
this.delegates = delegates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param processors 使用时请注意 DataSourceProcessor 的顺序
|
||||
*/
|
||||
public static DelegatingDataSourceProcessor with(DataSourceProcessor... processors) {
|
||||
FlexAssert.notEmpty(processors, "datasource processors");
|
||||
List<DataSourceProcessor> dataSourceProcessors = new ArrayList<>(Arrays.asList(processors));
|
||||
return new DelegatingDataSourceProcessor(dataSourceProcessors);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param processors 使用时请注意 DataSourceProcessor 的顺序
|
||||
*/
|
||||
public static DelegatingDataSourceProcessor with(List<DataSourceProcessor> processors) {
|
||||
FlexAssert.notEmpty(processors, "datasource processors");
|
||||
return new DelegatingDataSourceProcessor(processors);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String process(String dataSourceKey, Object mapper, Method method, Object[] arguments) {
|
||||
for (DataSourceProcessor delegate : delegates) {
|
||||
// 使用时请注意 DataSourceProcessor 的顺序,一旦匹配到处理器将进行中断处理并返回处理结果
|
||||
String returnKey = delegate.process(dataSourceKey, mapper, method, arguments);
|
||||
if (null != returnKey) return returnKey;
|
||||
}
|
||||
// 无可用的处理器策略,返回原始值
|
||||
return dataSourceKey;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package com.mybatisflex.core.datasource.processor;
|
||||
|
||||
import com.mybatisflex.processor.util.StrUtil;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 参数索引中取出数据源名称(针对简单类型参数快速解析读取)
|
||||
*
|
||||
* @author Alay
|
||||
* @since 2024-12-07 15:43
|
||||
*/
|
||||
public class ParamIndexDataSourceProcessor implements DataSourceProcessor {
|
||||
private static final String NULL_STR = "null";
|
||||
private static final String DYNAMIC_PREFIX = "#";
|
||||
private static final String INDEX_FIRST = "#first";
|
||||
private static final String INDEX_LAST = "#last";
|
||||
private static final String PARAM_INDEX = "#index";
|
||||
|
||||
/**
|
||||
* 若不符合处理逻辑将返回 null 值
|
||||
*/
|
||||
@Override
|
||||
public String process(String dataSourceKey, Object mapper, Method method, Object[] arguments) {
|
||||
if (StrUtil.isBlank(dataSourceKey)) return null;
|
||||
if (!dataSourceKey.startsWith(DYNAMIC_PREFIX)) return null;
|
||||
// 无效的参数
|
||||
if (arguments.length == 0) return null;
|
||||
|
||||
Integer index = null;
|
||||
if (INDEX_FIRST.equals(dataSourceKey)) index = 0;
|
||||
if (INDEX_LAST.equals(dataSourceKey)) index = arguments.length - 1;
|
||||
if (dataSourceKey.startsWith(PARAM_INDEX)) index = parseIndex(dataSourceKey);
|
||||
|
||||
// 没有符合约定的格式输入,则会返回 null
|
||||
if (null == index) return null;
|
||||
// 参数输入不合法(索引参数大于参数索引数)
|
||||
if (index >= arguments.length) return null;
|
||||
|
||||
// 参数中按照索引取出数值
|
||||
String value = String.valueOf(arguments[index]);
|
||||
if (StrUtil.isBlank(value) || NULL_STR.equals(value)) return null;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static Integer parseIndex(String dsKey) {
|
||||
// 参数索引
|
||||
String indexStr = dsKey.substring(PARAM_INDEX.length());
|
||||
if (indexStr.isEmpty()) return null;
|
||||
try {
|
||||
return Integer.parseInt(indexStr);
|
||||
} catch (NumberFormatException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 动态数据源注解@UseDataSource 的扩展
|
||||
*/
|
||||
package com.mybatisflex.core.datasource.processor;
|
||||
@ -26,6 +26,7 @@ import com.mybatisflex.core.row.RowMapper;
|
||||
import com.mybatisflex.core.table.TableInfo;
|
||||
import com.mybatisflex.core.table.TableInfoFactory;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
import com.mybatisflex.processor.util.StrUtil;
|
||||
import org.apache.ibatis.reflection.ExceptionUtil;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
|
||||
@ -61,7 +62,12 @@ public class FlexMapperProxy<T> extends MybatisMapperProxy<T> {
|
||||
finalDsKey = getMethodDsKey(method, proxy);
|
||||
}
|
||||
|
||||
//通过自定义分配策略去获取最终的数据源
|
||||
// 对数据源取值进行动态取值处理(#)
|
||||
if (!StrUtil.isBlank(finalDsKey)) {
|
||||
finalDsKey = DataSourceKey.processDataSourceKey(finalDsKey, proxy, method, args);
|
||||
}
|
||||
|
||||
// 通过自定义分配策略去获取最终的数据源
|
||||
finalDsKey = DataSourceKey.getShardingDsKey(finalDsKey, proxy, method, args);
|
||||
|
||||
if (StringUtil.hasText(finalDsKey) && !finalDsKey.equals(userDsKey)) {
|
||||
|
||||
@ -0,0 +1,106 @@
|
||||
package com.mybatisflex.spring.datasource.processor;
|
||||
|
||||
import com.mybatisflex.core.datasource.processor.DataSourceProcessor;
|
||||
import com.mybatisflex.processor.util.StrUtil;
|
||||
import org.springframework.context.expression.MethodBasedEvaluationContext;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.expression.BeanResolver;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.ParserContext;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* SpEL表达式支持处理器
|
||||
*
|
||||
* @author Alay
|
||||
* @since 2024-12-07 15:48
|
||||
*/
|
||||
public class SpelExpressionDataSourceProcessor implements DataSourceProcessor {
|
||||
/**
|
||||
* 动态表达式前缀
|
||||
*/
|
||||
private static final String DYNAMIC_PREFIX = "#";
|
||||
/**
|
||||
* 参数发现器
|
||||
*/
|
||||
private static final ParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
|
||||
/**
|
||||
* Express语法解析器
|
||||
*/
|
||||
private static final ExpressionParser PARSER = new SpelExpressionParser();
|
||||
|
||||
private BeanResolver beanResolver;
|
||||
/**
|
||||
* 解析上下文的模板 对于默认不设置的情况下,从参数中取值的方式 #param1
|
||||
* 设置指定模板 ParserContext. TEMPLATE_EXPRESSION{@link ParserContext#TEMPLATE_EXPRESSION} 后的取值方式: #{#param1}
|
||||
*/
|
||||
private ParserContext parserContext = new ParserContext() {
|
||||
|
||||
@Override
|
||||
public boolean isTemplate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExpressionPrefix() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExpressionSuffix() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@Override
|
||||
public String process(String dataSourceKey, Object mapper, Method method, Object[] arguments) {
|
||||
if (StrUtil.isBlank(dataSourceKey)) return null;
|
||||
if (!dataSourceKey.startsWith(DYNAMIC_PREFIX)) return null;
|
||||
if (arguments.length == 0) return null;
|
||||
|
||||
RootObject rootObject = new RootObject(method, arguments, mapper);
|
||||
StandardEvaluationContext context = new MethodBasedEvaluationContext(rootObject, method, arguments, NAME_DISCOVERER);
|
||||
context.setBeanResolver(beanResolver);
|
||||
final Object value = PARSER.parseExpression(dataSourceKey, parserContext).getValue(context);
|
||||
return value == null ? null : value.toString();
|
||||
}
|
||||
|
||||
|
||||
public void setBeanResolver(BeanResolver beanResolver) {
|
||||
this.beanResolver = beanResolver;
|
||||
}
|
||||
|
||||
public void setParserContext(ParserContext parserContext) {
|
||||
this.parserContext = parserContext;
|
||||
}
|
||||
|
||||
public static class RootObject {
|
||||
private final Method method;
|
||||
private final Object[] args;
|
||||
private final Object target;
|
||||
|
||||
public RootObject(Method method, Object[] args, Object target) {
|
||||
this.method = method;
|
||||
this.args = args;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public Object[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public Object getTarget() {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 动态数据源注解@UseDataSource 的扩展
|
||||
*/
|
||||
package com.mybatisflex.spring.datasource.processor;
|
||||
Loading…
x
Reference in New Issue
Block a user