springboot实现分库分表时动态切换数据库
在分库分表中,当主库挂了,需要将新插入的数据插入到从库中,可以通过动态地切换数据源来实现。在Spring Boot中,可以使用AbstractRoutingDataSource实现动态数据源切换。
具体实现步骤如下:
1、继承AbstractRoutingDataSource类,重写determineCurrentLookupKey()方法,根据具体的业务规则动态切换数据源,如下所示:
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
2、定义一个数据源上下文,用于保存当前使用的数据源的名称,如下所示:
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
//写入数据源
public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
}
//获取数据源
public static String getDataSource() {
return contextHolder.get();
}
//移除数据源
public static void clearDataSource() {
contextHolder.remove();
}
}
3、在代码中,根据具体业务规则,动态地设置当前使用的数据源的名称,如下所示:
// 在需要切换数据源的地方,设置当前使用的数据源的名称
DataSourceContextHolder.setDataSource("secondaryDataSource");
// 执行需要访问数据库的操作
...
// 操作完成后,清除当前使用的数据源的名称
DataSourceContextHolder.clearDataSource();
4、在配置文件中,定义数据源和动态数据源的配置,如下所示:
spring.datasource.primary.url=jdbc:mysql://localhost:3306/db1
spring.datasource.primary.username=root
spring.datasource.primary.password=123456
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/db2
spring.datasource.secondary.username=root
spring.datasource.secondary.password=123456
spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver
5、在代码中,使用@Primary注解标记主数据源的DataSource对象,使用@Qualifier注解指定从数据源的名称,如下所示:
@Configuration
public class DataSourceConfig {
@Primary
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "dataSource")
public DynamicDataSource dataSource(@Qualifier("primaryDataSource") DataSource primaryDataSource,
@Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("primaryDataSource", primaryDataSource);
targetDataSources.put("secondaryDataSource", secondaryDataSource);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);
dataSource.setDefaultTargetDataSource(primaryDataSource);
return dataSource;
}
}
以上就是在主库挂了时,动态地切换数据源的实现方法。需要注意的是,具体的切换方法。
注意:
上面的代码中需要动态设置当前需要的数据源
但是我们往往需要设置一个默认的数据源,以下是对DynamicDataSource 的改造;
public class DynamicDataSource extends AbstractRoutingDataSource {
// 设置默认数据源
public void setDefaultDataSource(DataSource defaultDataSource) {
super.setDefaultTargetDataSource(defaultDataSource);
}
@Override
protected Object determineCurrentLookupKey() {
// 返回当前线程绑定的数据源名称,如果为空则使用默认数据源
return StringUtils.isNotBlank(DataSourceContextHolder.getDataSource()) ?
DataSourceContextHolder.getDataSource() : getDefaultTargetDataSource();
}
}
这样当我调用需要的数据源执行清除方法后就切换回默认的数据源;