概述
某些场景下,业务需要使用到多个数据库,这种场景下需要使用动态数据源的方式来执行相关sql。
多数据属性配置
#tomcat port
server.port=8080
# 主数据源配置
master.datasource.driver-class-name=com.mysql.jdbc.Driver
master.datasource.url=jdbc:mysql://192.168.195.129:3306/practice?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowPublicKeyRetrieval=true
master.datasource.username=root
master.datasource.password=123456
# 从数据源配置
slave.datasource.driver-class-name=com.mysql.jdbc.Driver
slave.datasource.url=jdbc:mysql://192.168.195.129:3307/practice?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowPublicKeyRetrieval=true
slave.datasource.username=root
slave.datasource.password=123456
#datasource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.195.129:3306/practice?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=123456
线程设置使用哪个数据库
public class MyThreadLocal {
public static final ThreadLocal<String> local = new ThreadLocal<>();
public static String getDataSource() {
return local.get();
}
public static void setDataSource(String dataSource) {
local.set(dataSource);
}
}
自定义动态数据源类
package com.practice.tkmybatis.config;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 自定义动态数据源类用于在程序运行中切换数据源,并集成动态数据源的抽象父类
*/
public class MyDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return MyThreadLocal.getDataSource();
}
}
动态数据源配置
package com.practice.tkmybatis.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* 动态多数据源,这种方案我们需要自定义一个动态数据源的类,可以在程序运行过程中动态切换数据源。
* 缺点:
* 1、有很多的重复代码,例如springBoot中配置多个数据源的配置Bean;
* 2、切换主从进行读写操作时需要程序任务手动设置ThreadLocal中数据,这样就可能向从数据库中写数据;
* 3、如果有很多个从节点,那么我们没有办法进行负载均衡;
* 4、如果某个节点崩溃我们不能切换到另外一台节点中,不能故障转移;
* 注意:
* 如果需要解决上面的问题,我们需要自己写一套管理代码来动态的切换数据源进行读写分离,故障转移等。
**/
@Configuration
@MapperScan(basePackages = {"com.practice.tkmybatis.mapper"}, sqlSessionFactoryRef = "sqlSessionFactoryBean")
public class DataSourceConfig {
/**
* 主库属性
*/
@Value("${master.datasource.driver-class-name}")
private String masterDriverClassName;
@Value("${master.datasource.url}")
private String masterUrl;
@Value("${master.datasource.username}")
private String masterUserName;
@Value("${master.datasource.password}")
private String masterPassword;
/**
* 从库属性
*/
@Value("${slave.datasource.driver-class-name}")
private String slaveDriverClassName;
@Value("${slave.datasource.url}")
private String slaveUrl;
@Value("${slave.datasource.username}")
private String slaveUserName;
@Value("${slave.datasource.password}")
private String slavePassword;
/**
* 主库配置
*
* @return
*/
@Bean
public DruidDataSource masterDruidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(masterDriverClassName);
druidDataSource.setUrl(masterUrl);
druidDataSource.setUsername(masterUserName);
druidDataSource.setPassword(masterPassword);
return druidDataSource;
}
/**
* 从库配置
*
* @return
*/
@Bean
public DruidDataSource slaveDruidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(slaveDriverClassName);
druidDataSource.setUrl(slaveUrl);
druidDataSource.setUsername(slaveUserName);
druidDataSource.setPassword(slavePassword);
return druidDataSource;
}
/**
* 配置动态数据类
*
* @param masterDruidDataSource 主数据源
* @param slaveDruidDataSource 从数据源
* @return 动态数据源
*/
public MyDataSource myDataSource(DruidDataSource masterDruidDataSource, DruidDataSource slaveDruidDataSource) {
MyDataSource myDataSource = new MyDataSource();
myDataSource.setDefaultTargetDataSource(masterDruidDataSource);
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("master", masterDruidDataSource);
dataSourceMap.put("slave", slaveDruidDataSource);
myDataSource.setTargetDataSources(dataSourceMap);
return myDataSource;
}
public SqlSessionFactoryBean sqlSessionFactoryBean(MyDataSource myDataSource) {
final SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(myDataSource);
return sqlSessionFactoryBean;
}
// 测试方法
public static void main(String[] args) {
MyThreadLocal.setDataSource("master");
// 执行master库的逻辑
MyThreadLocal.setDataSource("slave");
// 执行slave库的逻辑
}
}
参考
MySQL集群-SpringBoot集成多数据源
SpringBoot集成AbstractRoutingDataSource实现动态切换多数据源