sharding分表应用笔记(三)——多数据源路由
目录1 背景
应用背景:物理数据源只有一个;对于部分数据量大的表实行按月分表处理,其他的表仍然保持原先的模式不变。因为sharding对一些sql语句可能有兼容问题,所以为了预防生产问题,决定直接通过原数据源对未分表的表格进行操作,只有特定的表格才通过sharding数据源进行操作。所以需要进行多数据源路由。本篇记录sharding分表的多数据源路由实现。
环境:spring
2 配置
2.1 命名空间配置
<!-- 多数据源配置 -->
<bean id="multipleDataSource" class="com.example.dbConfig.MultipleDataSource">
<!-- 默认数据源 -->
<property name="defaultTargetDataSource" ref="dataSource" />
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="DEFAULT" value-ref="dataSource"/>
<entry key="SHARDING" value-ref="shardingDataSource"/>
</map>
</property>
</bean>
之后用多数据源的BeanId(multipleDataSource)替换各种数据库配置中原先的数据源BeanId(dataSource)
2.2 spring-jdbc路由配置
继承实现spring-jdbc的AbstractRoutingDataSource多数据源路由接口。实现该接口后,每次进行数据库操作时会通过determineCurrentLookupKey方法获取当前设定的数据库key,然后通过命名空间配置的映射启用对应的数据源。
/**
* @ClassName: MultipleDataSource
* @Description: 数据源路由实现类
* @Author: nagiumi
* @Date: 2023/6/5
* @Version:1.0
*/
public class MultipleDataSource extends AbstractRoutingDataSource {
private static final Logger log = LoggerFactory.getLogger(MultipleDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
String dataSourceConfig = MultipleDataSourceHandler.getRouteKey();
return dataSourceConfig;
}
}
/**
* @ClassName: MultipleDataSourceHandler
* @Description: 多数据源持有类,数据源路由
* @Author: nagiumi
* @Date: 2023/6/5
* @Version:1.0
*/
public class MultipleDataSourceHandler {
private static final Logger logger = LoggerFactory.getLogger(MultipleDataSourceHandler.class);
/**
* 线程副本,保证线程安全
*/
private static ThreadLocal<String> routeKey = new ThreadLocal<String>();
/**
*@Description: 绑定当前线程数据源路由的key 使用完成后必须调用removeRouteKey()方法删除
*@Param: * @Param: key
*@return:
*@Author: nagiumi
*@date: 2023/6/5
*/
public static void setRouteKey(String key){
logger.warn("系统切换到[{}]数据源",key);
routeKey.set(key);
}
/**
*@Description: 获取当前线程的路由
*@Param: * @Param: null
*@return:
*@Author: nagiumi
*@date: 2023/6/5
*/
public static String getRouteKey(){
return routeKey.get();
}
/**
*@Description: 删除与当前线程绑定的路由key
*@Param: * @Param: null
*@return:
*@Author: nagiumi
*@date: 2023/6/5
*/
public static void removeRouteKey(){
logger.warn("系统重置数据源");
routeKey.remove();
}
}
3 指定路由
3.1 自定义注解
自定义一个指定路由数据库的注解,通过在表的service类上添加注解来完成对应表的指定路由操作。
数据源枚举
public interface ContextConstant {
/**
*@Description: 数据源枚举
*@Param: * @Param: null
*@return:
*@Author: nagiumi
*@date: 2023/6/5
*/
enum DataSourceType {
DEFAULT, SHARDING
}
}
定义注解
/**
* @ClassName: ContextConstant
* @Description: 自定义切换数据源注解
* @Author: nagiumi
* @Date: 2023/6/5
* @Version:1.0
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
/**
*@Description: 默认为上报数据库数据源
*@Param: * @Param: null
*@return:
*@Author: nagiumi
*@date: 2023/6/5
*/
ContextConstant.DataSourceType value() default ContextConstant.DataSourceType.DEFAULT;
}
3.2 功能实现
定义aop进行注解扫描,然后反射获取注解指定的路由
/**
* @ClassName: DynamicDataSourceAspect
* @Description: 切换数据源类
* @Author: nagiumi
* @Date: 2023/6/5
* @Version:1.0
* 设置为1,优先加载,优先级高于AbstractRoutingDataSource的determineCurrentLookupKey()
*
*/
@Component
@Aspect
@Order(1)
public class DynamicDataSourceAspect {
private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
/**
*@Description: 前置切面
*@Param: * @Param: null
*@return:
*@Author: nagiumi
*@date: 2023/6/5
*/
@Before("execution(* com.example.business..*.*(..))")
public void before(JoinPoint point) {
try {
DataSource annotationOfClass = point.getTarget().getClass().getAnnotation(DataSource.class);
String methodName = point.getSignature().getName();
Class[] parameterTypes = ((MethodSignature) point.getSignature()).getParameterTypes();
Method method = point.getTarget().getClass().getMethod(methodName, parameterTypes);
DataSource methodAnnotation = method.getAnnotation(DataSource.class);
methodAnnotation = methodAnnotation == null ? annotationOfClass : methodAnnotation;
ContextConstant.DataSourceType dataSourceType = methodAnnotation != null
&& methodAnnotation.value() != null ? methodAnnotation.value() : null;
if (Objects.nonNull(dataSourceType)) {
log.warn("aspect 准备切换数据源,发起方:{}", point.getTarget().getClass().getName() + "#" + methodName);
MultipleDataSourceHandler.setRouteKey(dataSourceType.name());
}
} catch (NoSuchMethodException e) {
log.error("aspect error,err={}",e);
}
}
/**
*@Description: 后置切面
*@Param: * @Param: null
*@return:
*@Author: nagiumi
*@date: 2023/6/5
*/
@After("execution(* com.example.business..*.*(..))")
public void after(JoinPoint point) {
String oldKey = MultipleDataSourceHandler.getRouteKey();
if (StringUtils.isNotBlank(oldKey)) {
MultipleDataSourceHandler.removeRouteKey();
}
}
}
3.3 用例
@Service
@DataSource(ContextConstant.DataSourceType.SHARDING)
public class RepaymentLogBizService {
@Autowired
private RepaymentLogMapper repaymentLogMapper;
//业务方法
}
标签:Author,数据源,nagiumi,Param,分表,sharding,public,路由
From: https://www.cnblogs.com/nagiumi-misaka/p/17843259.html