之前也实现了多数据源功能,是将包路径绑定对应的数据源,调用该包下的方法会走对应的数据库,这种方法不太灵活,后面我改造了一下,可实现动态切换数据源。
之前实现多数据源写法:
项目结构
1.数据源配置文件
在application.yml配置文件中配置两个数据源,如下:
datasource:
pre :
jdbc-url: jdbc:mysql://localhost:3306/xiaxia1?useUnicode=true&characterEncoding=utf8&characterResultSets=utf8
username: root
password: 123456
sit :
jdbc-url: jdbc:mysql://localhost:3306/xiaxia2?useUnicode=true&characterEncoding=utf8&characterResultSets=utf8
username: root
password: 123456
注:按层次结构写的配置文件,一定要注意缩进以及表达的含义。
2.修改springboot服务启动文件:Application.java
// exclude = {DataSourceAutoConfiguration.class} 排除主动注入数据源
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
//@EnableTransactionManagement // 开启注解事务管理,等同于xml配置文件中的 <tx:annotation-driven />
//自动扫描包路径下面的所有@Controller、@Service、@Repository、@Component 的类,
// 并把符合扫描规则的类装配到spring容器中。
@ComponentScan(basePackages = {"com.xiateng"})
// 原来扫描dao层接口
@MapperScan({"com.xiateng.dao.user","com.xiateng.dao.userbuyer"})
public class Application extends WebMvcConfigurerAdapter{
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SessionInterceptor()).excludePathPatterns(Arrays.asList("/res/**"));
}
}
配置说明:exclude = {DataSourceAutoConfiguration.class}
exclude,排除此类的AutoConfig,即禁止 SpringBoot 自动注入数据源配置,DataSourceAutoConfiguration.class 会自动查找 application.yml 或者 properties 文件里的 spring.datasource.* 相关属性并自动配置单数据源,因为这里我们需要手动配置多数据源
3.配置两个数据源(数据源个数看自己需要)
@Configuration
public class DataSourceConfig {
// 表示这个数据源是默认数据源
// @Primary
// 将这个对象放入spring容器中(交给Spring管理)
@Bean(name="preDataSource")
// 读取 application.yml 中的配置参数映射成为一个对象
@ConfigurationProperties(prefix = "spring.datasource.pre")
public DataSource getDataSource1(){
// 创建一个数据源
return DataSourceBuilder.create().build();
}
// @Primary
@Bean(name="sitDataSource")
@ConfigurationProperties(prefix = "spring.datasource.sit")
public DataSource getDataSource2(){
return DataSourceBuilder.create().build();
}
}
4.数据源路由类,用于动态获取数据源
数据源上下文,用于保存跟获取Spring容器中的数据源Bean
/**
* 数据源上下文
*/
public class DataSourceContext {
// 用于记录每个线程需要使用的数据源关键字。并提供切换、读取、清除数据源配置信息的方法
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSource(String value) {
contextHolder.set(value);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
/**
* DataSource 路由类
* 重写的函数决定了最后选择的DateSource
*/
public class MultiRouteDataSource extends AbstractRoutingDataSource{
@Nullable
@Override
protected Object determineCurrentLookupKey() {
// 通过绑定线程的数据源上下文实现多数据源的动态切换,有兴趣的可以去查阅资料或源码
return DataSourceContext.getDataSource();
}
}
AbstractRoutingDataSource说明
AbstractRoutingDataSource是spring-jdbc包提供的一个了AbstractDataSource的抽象类,它实现了DataSource接口的用于获取数据库连接的方法。
AbstractRoutingDataSource的内部维护了一个名为targetDataSources的Map,并提供的setter方法用于设置数据源关键字与数据源的关系,实现类被要求实现其determineCurrentLookupKey()方法,由此方法的返回值决定具体从哪个数据源中获取连接。
AbstractRoutingDataSource提供了程序运行时动态切换数据源的方法,在dao类或方法上标注需要访问数据源的关键字,路由到指定数据源,获取连接。
五.数据源配置类(注入数据源),设置多个数据源,同时设置默认数据源
@Configuration
public class DataSourceComponent {
@Autowired
@Qualifier("preDataSource")
private DataSource preDataSource;
@Autowired
@Qualifier("sitDataSource")
private DataSource sitDataSource;
@Primary//不加这个会报错。
@DependsOn({ "preDataSource", "sitDataSource"}) //解决数据库循环依赖问题
@Bean(name = "multiDataSource")
public MultiRouteDataSource exampleRouteDataSource() {
MultiRouteDataSource multiDataSource = new MultiRouteDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("preDataSource", preDataSource);
targetDataSources.put("sitDataSource", sitDataSource);
multiDataSource.setTargetDataSources(targetDataSources);
multiDataSource.setDefaultTargetDataSource(preDataSource);
return multiDataSource;
}
}
写到这里大家会有个疑问,为什么要同时用的两个注解@Autowired和@Qualifier("preDataSource"),直接用一个不就可以吗,答案是否定的。
@Autowired : 从Spring容器中找到类型为DataSource的类,将它注入进来。
@Qualifier("preDataSource") : 指定我们需要装配的Bean。
了解了这两个注解的功能后我恍然大悟,我配置多数据源的时候,DataSource这个类在Spring容器中是存在两个不同名称的Bean的(preDataSource和sitDataSource),如果直接用@Autowired注解,Spring不知道要装配哪个Bean,项目启动就会报错,而@Qualifier("preDataSource")可以指定装配哪个Bean,这样就理解了。。。。
六.测试
public int multiRouteTest(TtUser record) {
// 切换数据源为sitDataSource(我之前配置默认数据源是preDataSource)
DataSourceContext.setDataSource("sitDataSource");
int i = 0;
try {
record.setUpdatedBy(54543L);
i = ttUserMapper.updateByPrimaryKeySelective(record);
}catch (Exception e){
e.printStackTrace();
}
return i;
}
这样就实现了数据源动态切换 ☺☺☺☺☺☺