springboot + mybatisplus项目中,怎么实现动态切换数据源,要求是不能在yml文件中将数据库连接写死,根据业务需求,服务器中每过一个月会自动生成一个数据库用来存储数据,在这个库中每天会按照日期去生成一张表,我怎么能动态的切换到我想要查询的数据库??有大佬知道不
试过使用aop切面去实现功能,自定义类去继承AbstractRoutingDataSource,但是没有用
springboot + mybatisplus项目中,怎么实现动态切换数据源,要求是不能在yml文件中将数据库连接写死,根据业务需求,服务器中每过一个月会自动生成一个数据库用来存储数据,在这个库中每天会按照日期去生成一张表,我怎么能动态的切换到我想要查询的数据库??有大佬知道不
试过使用aop切面去实现功能,自定义类去继承AbstractRoutingDataSource,但是没有用
**核心解决方案:动态数据源路由 + 表名处理器**
1. 实现动态数据源路由(关键代码):
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();
private final Map<Object, Object> dynamicTargetDataSources = new ConcurrentHashMap<>();
@Override
protected Object determineCurrentLookupKey() {
return CONTEXT.get();
}
public void addDataSource(String key, DataSource dataSource) {
dynamicTargetDataSources.put(key, dataSource);
super.setTargetDataSources(dynamicTargetDataSources);
super.afterPropertiesSet(); // 必须触发刷新
}
public static void setDataSourceKey(String key) {
CONTEXT.set(key);
}
public static void clearDataSourceKey() {
CONTEXT.remove();
}
}
2. 配置主数据源(至少需要配置一个默认数据源):
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DynamicDataSource dynamicDataSource(DataSource masterDataSource) {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
dynamicDataSource.setTargetDataSources(new HashMap<>());
return dynamicDataSource;
}
}
3. AOP切面实现动态切换:
@Aspect
@Component
public class DataSourceAspect {
@Around("@annotation(ds)")
public Object around(ProceedingJoinPoint point, TargetDataSource ds) throws Throwable {
String dbName = generateDbName(); // 根据业务生成数据库名
String key = ds.value() + "_" + dbName;
if (!dynamicDataSourceExists(key)) {
createNewDataSource(key); // 动态创建新数据源
}
DynamicDataSource.setDataSourceKey(key);
try {
return point.proceed();
} finally {
DynamicDataSource.clearDataSourceKey();
}
}
private void createNewDataSource(String key) {
DataSource newDataSource = DataSourceBuilder.create()
.driverClassName("com.mysql.cj.jdbc.Driver")
.url("jdbc:mysql://ip:port/" + key)
.username("root")
.password("123456")
.build();
dynamicDataSource.addDataSource(key, newDataSource);
}
}
4. 动态表名处理(MyBatis-Plus配置):
public class DynamicTableNameHandler implements TableNameHandler {
@Override
public String dynamicTableName(String sql, String tableName) {
return tableName + "_" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
}
}
@Configuration
public class MybatisPlusConfig {
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> {
DynamicTableNameHandler dynamicTableNameHandler = new DynamicTableNameHandler();
configuration.setSqlParser(new JsqlParserCollection());
configuration.getGlobalConfig().getTableName()
.setTableNameHandler((sql, tableName) -> {
if (tableName.startsWith("log_")) {
return dynamicTableNameHandler.dynamicTableName(sql, tableName);
}
return tableName;
});
};
}
}
**关键点说明:**
1. 数据源动态注册:通过`addDataSource`方法实时注册新数据源到Spring容器
2. 连接池管理:建议使用HikariCP并在创建时配置连接池参数
3. 多级缓存:使用LRU策略缓存最近使用的数据源,避免频繁创建
4. 事务管理:需要在切面中处理事务边界,建议使用`@Transactional(propagation = Propagation.REQUIRES_NEW)`
5. 元数据管理:建议维护一个系统表记录所有生成的数据库和表信息
**典型使用场景:**
@Service
public class LogService {
@TargetDataSource("log") // 自定义注解
public List<Log> getTodayLogs() {
// 会自动路由到log_202308数据库的log_20230801表
return logMapper.selectList(Wrappers.emptyWrapper());
}
}
你要的效果:
每个月自动创建新数据库,每天自动建新表,查询时自动切换到对应月份的库和当天表。
实现步骤:
动态创建数据库和表
动态切换数据库的核心代码
MyBatis-Plus动态表名处理
用AOP自动切换库和表
使用示例
注意事项:
第一次访问新库时会自动创建数据源
需要提前确保数据库用户有创建库/表的权限
生产环境建议用连接池(如HikariCP)
事务中切换数据源会有问题,建议在事务外层切换
这个方案的核心思想:通过AOP在每次查询前,根据当前时间算出要用的库和表,然后动态设置到数据源路由和表名处理器中。