文章目录
- 背景
- 多数据源准备工作
- 整体思路
- 多数据源切换方式
- 准备工作
- 自动切换(@DS方式)
- 配置文件设置
- 手动切换
- 配置文件设置
- 项目启动加载数据源:使用注解@PostConstruct
- 添加数据源
- 总结
背景
最近公司项目中需要做多租户进行数据源切换的业务,目的是要达到租户与租户之间的数据要进行隔离,本次实现的实现是在一台服务器中,去进行多个数据库之间的切换,且不能相互影响。有不明白多租户的相关内容,可以查看资料:
多租户相关内容
多数据源准备工作
参考资料:https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611
整体思路
在请求发出的时候就去进行拦截(判断)这个用户所在的数据源,然后再去切换到对应的数据源中。
1、在我们的业务中,用户可以自己注册,然后就会生产一个数据库,那么在新用户进行登录的时候就要去实时的切换数据源。
2、那么对于已有的数据源,在项目启动的时候就去把所有的数据源加载到内存中。
多数据源切换方式
在数据源切换中,找到有两种切换方式一个自动,另外一个是手动切换。
准备工作
添加依赖:使用最新版本
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
<version>${version}</version>
</dependency>
自动切换(@DS方式)
配置文件设置
自动切换也就是在配置文件中把需要用到的数据源准备好,这样就可以在使用的时候在注解上进行使用并进行切换。
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
master: # 数据源名称
url: jdbc:mysql://116.204.118.226:3306/test1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: guyanshuang.
driver-class-name: com.mysql.cj.jdbc.Driver
# 如下,如果你是确定的几个数据源,可以直接都在yaml配置写死即可
slave_1:
url: jdbc:mysql://116.204.118.226:3306/test2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
切换数据源代码
@RestController
@RequestMapping("/eee")
public class DataSourceTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private DataSource dataSource;
private DataSourceCreator dataSourceCreator;
@DS("master")
@GetMapping("/selectAll")
public List selectAll() {
return jdbcTemplate.queryForList("select * from test");
}
@GetMapping("/selectByCondition")
@DS("slave_1")
public List selectByCondition() {
return jdbcTemplate.queryForList("select * from test");
}
这里有两个方法,都使用@DS注解,但是他们的参数是不同的,这里也就是去切换到对应的数据源了。
@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。
手动切换
配置文件设置
这里是使用了druid的配置
# 数据源配置
spring:
datasource:
druid:
destroy-method: close
stat-view-servlet:
enabled: true
login-username: admin
login-password: 123456
dynamic:
primary: master
# /p6spy: true
lazy: true
# 配置全局druid参数,请按需配置
druid:
initial-size: 5
max-active: 20
min-idle: 5
max-wait: 60000
datasource:
master:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/auth?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
项目启动加载数据源:使用注解@PostConstruct
@PostConstruct
public void initDataSource(){
//查询所有的资源
List<TanentModel> tanentModels =tantentMapper.queryAllDataSource();
for (TanentModel tantent:tanentModels) {
DataSourceProperty dataSourceProperty = new DataSourceProperty();
dataSourceProperty.setUrl(tantent.getTenantUrl());
dataSourceProperty.setDriverClassName(tantent.getTenantDriverClassName());
dataSourceProperty.setUsername(tantent.getTenantUserName());
dataSourceProperty.setPassword(tantent.getTenantUserPassword());
dataSourceProperty.setPoolName(tantent.getTenantCode());
//创建数据源
javax.sql.DataSource createDataSource = dataSourceCreator.createDataSource(dataSourceProperty);
//数据源添加到资源池中
((DynamicRoutingDataSource)dataSource).addDataSource(dataSourceProperty.getPoolName(),createDataSource);
}
}
添加数据源
在这里插入代码片 public void switchDataSource(String phone) {
TanentModel tanentModel=null;//获取到的数据源
try {
if (!StringUtils.isEmpty(phone)){
tanentModel= tantentMapper.queryDataSource(phone);
DataSourceProperty dataSourceProperty = new DataSourceProperty();
dataSourceProperty.setUrl(tanentModel.getTenantUrl());
dataSourceProperty.setDriverClassName(tanentModel.getTenantDriverClassName());
dataSourceProperty.setUsername(tanentModel.getTenantUserName());
dataSourceProperty.setPassword(tanentModel.getTenantUserPassword());
dataSourceProperty.setPoolName(tanentModel.getTenantCode());
String poolName=tanentModel.getTenantCode();
DynamicDataSourceContextHolder.clear();
//切换到对应poolName的数据源
// 判断数据源是否存在,不存在则添加
javax.sql.DataSource createDataSource = dataSourceCreator.createDataSource(dataSourceProperty);
//根据手机号查到的资源名称,获取资源池中的资源
DataSource dataSourcePool = ((DynamicRoutingDataSource) this.dataSource).getDataSources().get(poolName);
//如果资源为空,说明资源池中没有改资源,那么就添加到资源池中
if (dataSourcePool==null){
//动态添加到资源池
((DynamicRoutingDataSource)dataSource).addDataSource(dataSourceProperty.getPoolName(),createDataSource);
}
DynamicDataSourceContextHolder.push(poolName);
String peek = DynamicDataSourceContextHolder.peek();
System.out.println("当前线程数据源:"+peek);
}
}catch (Exception e){
e.printStackTrace();
}
}
添加数据源核心代码:
DynamicDataSourceContextHolder.push(poolName);
总结
多数据源切换是现代应用开发中的重要功能之一。对于数据源切换,我们可以采取两种方式:@DS切换和手动切换。
@DS切换是一种自动化方式,通过在代码中配置切换规则,系统可以根据预设条件自动切换数据源。这种方式能够减少人工操作的繁琐,提高效率和准确性。它适用于在特定场景下需要频繁切换数据源的情况,例如负载均衡、读写分离等。通过@DS切换,我们可以灵活地管理和调度多个数据源,实现资源的优化利用和负载均衡,从而提升系统的性能和可扩展性。
另一方面,手动切换是一种更加灵活的方式。它允许开发人员根据实际情况进行即时切换,通过编码的方式直接指定使用哪个数据源。这种方式适用于需要根据具体需求灵活切换数据源的场景,例如特定业务逻辑需要使用不同的数据源等。手动切换虽然需要人工介入,但它提供了更大的灵活性和可控性,使开发人员可以根据需要自由切换数据源,满足不同的业务需求。
综上所述,针对多数据源切换,我们可以根据具体业务需求和系统特点选择合适的切换方式。@DS切换适用于频繁切换数据源、需要自动化调度的场景,而手动切换则更加灵活,适用于需要根据实际情况即时切换数据源的情况。通过合理的规划和技术实施,我们可以充分发挥各种切换方式的优势,实现数据源的灵活管理和高效切换,为业务运行提供有力支持。