Springboot dynamically switches data sources
- Introduce Alibaba's druid component
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.2.0</version>
</dependency>
- In the yml file of the spring project, add the database information. My project has added the taos database. Of course, bloggers can remove it.
spring:
datasource:
tdengine-server:
jdbc-url: jdbc:TAOS://127.0.0.1:6030/valid_time?timezone=Asia/Shanghai&charset=utf-8
driver-class-name: com.taosdata.jdbc.TSDBDriver
username: root
password: taosdata
druid:
datasource:
master:
jdbc-url: jdbc:mysql://127.0.0.1:3306/valid_project?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.jdbc.Driver
username: root
password: Shutd0wn
slave:
jdbc-url: jdbc:mysql://127.0.0.1:3306/valid_project_test?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.jdbc.Driver
username: root
password: Shutd0wn
- Create the DataSourceProperties class
@ConfigurationProperties(prefix = "spring.druid.datasource")
@Component
@Getter
@Setter
public class DataSourceProperties {
private Map<String,String> master;
private Map<String,String> slave;
}
- Create a data source switching configuration class DynamicDataSourceConfig
@Configuration
@MapperScan(basePackages = {
"com.sinux.sinuxproject.dao.mysql"})
public class DynamicDataSourceConfig {
@Resource
private DataSourceProperties dataSourceProperties;
@Bean
public DataSource master(@Autowired DataSourceProperties dataSourceProperties){
DruidDataSource druidDataSource = new DruidDataSource();
Map<String, String> master = dataSourceProperties.getMaster();
druidDataSource.setUsername(master.get("username"));
druidDataSource.setPassword(master.get("password"));
druidDataSource.setUrl(master.get("jdbc-url"));
return druidDataSource;
}
@Bean
public DataSource slave(@Autowired DataSourceProperties dataSourceProperties){
DruidDataSource druidDataSource = new DruidDataSource();
Map<String, String> slave = dataSourceProperties.getSlave();
druidDataSource.setUsername(slave.get("username"));
druidDataSource.setPassword(slave.get("password"));
druidDataSource.setUrl(slave.get("jdbc-url"));
return druidDataSource;
}
@Bean("dataSource")
@Primary
public DynamicDataSource dataSource(DataSource master,DataSource slave){
Map<Object,Object>map = new HashMap<>(4);
map.put("master",master);
map.put("slave",slave);
return new DynamicDataSource(master,map);
}
@Primary
@Bean("sqlSessionFactoryBean")
public SqlSessionFactory sqlSessionFactoryBean(@Qualifier("dataSource") DynamicDataSource dynamicDataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/mysql/**/*Mapper.xml"));
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true);
configuration.setDefaultFetchSize(100);
configuration.setDefaultStatementTimeout(30);
sqlSessionFactoryBean.setConfiguration(configuration);
return sqlSessionFactoryBean.getObject();
}
@Bean(name = "mysqlTransactionManager")
@Primary
public DataSourceTransactionManager mysqlTransactionManager(@Qualifier("dataSource")DynamicDataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}
@Bean(name = "mysqlSqlSessionTemplate")
@Primary
public SqlSessionTemplate mysqlSqlSessionTemplate(@Qualifier("sqlSessionFactoryBean")SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
- Create DynamicDataSource to inherit AbstractRoutingDataSource, rewrite determineCurrentLookupKey method, and realize data source switching
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final TransmittableThreadLocal<String> contextHolder = new TransmittableThreadLocal<>();
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
}
public static String getDataSource() {
System.out.println(contextHolder.get());
String key = contextHolder.get()==null?"master":contextHolder.get();
return key;
}
public static void clearDataSource() {
contextHolder.remove();
}
}
- Exclude automatic configuration of startup classes
@SpringBootApplication(exclude= {
DataSourceAutoConfiguration.class})
- To use, you only need to switch the data source before using it. I use aop to intercept and judge
DynamicDataSource.setDataSource("slave");