核心类AbstractRoutingDataSource
- 核心类AbstractRoutingDataSource
继承该类实现determineCurrentLookupKey方法来决定数据源
package com.tom.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import java.util.Map;
/**
* 扩展 Spring 的 AbstractRoutingDataSource 抽象类,重写 determineCurrentLookupKey 方法
* 动态数据源
* determineCurrentLookupKey() 方法决定使用哪个数据源
*/
public class DynamicDataSourceContext extends AbstractRoutingDataSource {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 动态刷新数据源列表,//TODO 生产开发时需要优化
*
* @param targetDataSources
*/
public void freshDataSources(Map<Object, Object> targetDataSources) {
//设置一个默认数据
super.setDefaultTargetDataSource(targetDataSources.get(DbConsts.DEFAULT_KEY));
//设置所有数据源
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
public static void setDataSource(String dataSource) {
CONTEXT_HOLDER.set(dataSource);
}
public static String getDataSource() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSource() {
CONTEXT_HOLDER.remove();
}
}
- 初始化数据源
public class DbConsts {
public static final String DEFAULT_KEY="default_db";
public static final String DEFAULT_DB2="tenant_db2";
public static final String DEFAULT_DB3="tenant_db3";
}
/**
* 配置多数据源
*/
@Configuration
public class DynamicDataSourceConfig {
@Bean
@Primary
public DynamicDataSourceContext dataSource() {
Map<Object, Object> targetDataSources = getDataSources();
DynamicDataSourceContext dynamicDataSourceContext = new DynamicDataSourceContext();
dynamicDataSourceContext.freshDataSources(targetDataSources);
return dynamicDataSourceContext;
}
/**
* 构建初始化的数据源
* @return
*/
public Map<Object, Object> getDataSources() {
DataSourceItem ds1 = DataSourceItem.builder().key(DbConsts.DEFAULT_KEY).url("jdbc:mysql://localhost:3306/db1?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8").username("root").password("123456").driverClassName("com.mysql.jdbc.Driver").build();
DataSourceItem ds2 = DataSourceItem.builder().key(DbConsts.DEFAULT_DB2).url("jdbc:mysql://localhost:3306/db2?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8").username("root").password("123456").driverClassName("com.mysql.jdbc.Driver").build();
Map<Object, Object> map = new HashMap<>();
map.put(DbConsts.DEFAULT_KEY, buildDataSource(ds1));
map.put(DbConsts.DEFAULT_DB2, buildDataSource(ds2));
return map;
}
/**
* 构建 新增的数据源
* @return
*/
public Map<Object, Object> getDataSourcesNew() {
DataSourceItem ds1 = DataSourceItem.builder().key(DbConsts.DEFAULT_KEY).url("jdbc:mysql://localhost:3306/db1?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8").username("root").password("123456").driverClassName("com.mysql.jdbc.Driver").build();
DataSourceItem ds2 = DataSourceItem.builder().key(DbConsts.DEFAULT_DB2).url("jdbc:mysql://localhost:3306/db2?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8").username("root").password("123456").driverClassName("com.mysql.jdbc.Driver").build();
DataSourceItem ds3 = DataSourceItem.builder().key(DbConsts.DEFAULT_DB3).url("jdbc:mysql://localhost:3306/db3?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8").username("root").password("123456").driverClassName("com.mysql.jdbc.Driver").build();
Map<Object, Object> map = new HashMap<>();
map.put(DbConsts.DEFAULT_KEY, buildDataSource(ds1));
map.put(DbConsts.DEFAULT_DB2, buildDataSource(ds2));
map.put(DbConsts.DEFAULT_DB3, buildDataSource(ds3));
return map;
}
/**
* 构建数据源
*
* @param item
* @return
*/
private DataSource buildDataSource(DataSourceItem item) {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(item.getUrl());
dataSource.setUsername(item.getUsername());
dataSource.setPassword(item.getPassword());
dataSource.setDriverClassName(item.getDriverClassName());
return dataSource;
}
}
UserController
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
DynamicDataSourceContext dataSource;
@Autowired
DynamicDataSourceConfig config;
@GetMapping("/find1")
public User find1() {
//不设置数据源,则使用默认数据源
User user = userService.getById(1);
System.out.println(user);
return user;
}
@GetMapping("/find2")
public User find2() {
//设置数据源为db3
DynamicDataSourceContext.setDataSource(DbConsts.DEFAULT_DB2);
User user = userService.getById(2);
System.out.println(user);
return user;
}
@GetMapping("/find3")
public User find3() {
//设置数据源为db3
DynamicDataSourceContext.setDataSource(DbConsts.DEFAULT_DB3);
User user = userService.getById(3);
System.out.println(user);
return user;
}
/**
* 刷新数据源
*
* @return
*/
@GetMapping("/freshDb")
public int freshDb() {
Map<Object, Object> dataSourcesNew = config.getDataSourcesNew();
dataSource.freshDataSources(dataSourcesNew);
return 1;
}
}
db 准备
建立三个数据源db1,db2,db3
并往三个数据源里添加数据
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint NOT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- db1
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'user1_db1');
INSERT INTO `user` VALUES ('2', 'user2_db1');
INSERT INTO `user` VALUES ('3', 'user3_db1');
-- ----------------------------
-- db2
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'user1_db2');
INSERT INTO `user` VALUES ('2', 'user2_db2');
INSERT INTO `user` VALUES ('3', 'user3_db2');
-- ----------------------------
-- db3
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'user1_db3');
INSERT INTO `user` VALUES ('2', 'user2_db3');
INSERT INTO `user` VALUES ('3', 'user3_db3');
效果
http://localhost:8080/user/find1
{"id":1,"name":"user1_db1"}
http://localhost:8080/user/find3
这里因为初始化默认的数据库只有db1,db2,所以此时 DynamicDataSourceContext.setDataSource(DbConsts.DEFAULT_DB3);是设置了一个不存在的数据源,也会从默认数据源中读取;
{"id":3,"name":"user3_db1"}
执行刷新数据源后:
http://localhost:8080/user/freshDb
再访问http://localhost:8080/user/find3 可以看到数据源是从db3读取回来的;
{"id":3,"name":"user3_db3"}
代码仓库地址:https://gitee.com/zwbeyond/dynamic-data-source.git