1、多数据源与动态数据源
当项目不只是要用到一个数据库的时候就需要使用到多个数据源了,这种场景很多,比如要查找的数据不在同一个数据库库中,或者是要做数据库读写分离。应对上面的问题主要有两种解决方法。
第一种:在Spring中注入多个DataSource,然后根据不同的DataSource生成不同的SqlSessionFactory,把要操作不同数据库的mapper接口放在不同的包下,并且使用MyBatis的@MapperScan注解,使用不同的SqlSessionFactory扫描不同包下的mapper接口。
例如:
第一步:向Spring中注入多个数据源
@Bean("db1-DataSource")
@Primary//注意这个@Primary注解是必须的,必须要有一个数据源加上这个注解,否则SpringBoot无法启动
@ConfigurationProperties(prefix = "datasource.db1")
fun db1DataSource(): DataSource {
return DataSourceBuilder.create().build()
}
@Bean("db2-DataSource")
@ConfigurationProperties(prefix = "datasource.db2")
fun db2DataSource(): DataSource {
return DataSourceBuilder.create().build()
}
第二步:生成多个SqlSessionFactory并注入Spring
@Bean("db1-SqlSessionFactory")
fun db1SqlSessionFactory(@Qualifier("db1-DataSource") ds: DataSource): SqlSessionFactory {
val fb = SqlSessionFactoryBean()
fb.setDataSource(ds)
fb.setMapperLocations("你的mapper的xml文件位置")
return fb.getObject()
}
@Bean("db2-SqlSessionFactory")
fun db2SqlSessionFactory(@Qualifier("db2-DataSource") ds: DataSource): SqlSessionFactory {
val fb = SqlSessionFactoryBean()
fb.setDataSource(ds)
fb.setMapperLocations("你的mapper的xml文件位置")
return fb.getObject()
}
第三步:@MapperScan使用不同的SqlSessionFactory扫描不同包下的mapper接口生成代理对象,假设要操作db1数据库的mapper接口放在app.mapper.db1包下,要操作db2数据库的mapper接口放在app.mapper.db2包下
@MapperScan(value = ["app.mapper.db1"], sqlSessionFactoryRef = "db1-SqlSessionFactory")
@MapperScan(value = ["app.mapper.db2"], sqlSessionFactoryRef = "db2-SqlSessionFactory")
这样不同包下的mapper接口就可以操作不同的数据库了。
接下来详细介绍第二种方法。
第二种:使用Spring提供的动态数据源AbstractRoutingDataSource
AbstractRoutingDataSource是一个抽象类,他实现了DataSource接口,内部可以存放多个DataSource,可以在需要的时候返回不同的DataSource。
接下来解析AbstractRoutingDataSource的部分源码:
//AbstractRoutingDataSource的内部使用了一个map存放多个数据源,key是数据源的唯一名字(可以任意命名,但是要保证唯一),value是对应的DataSource
private Map<Object, Object> targetDataSources;
//提供一个默认使用的数据源
private Object defaultTargetDataSource;
//这个是我们要实现的一个抽象方法,返回值是DataSource的唯一名字,表示使用该名字对应的DataSource
protected abstract Object determineCurrentLookupKey();
//这个是决定使用哪个数据源的方法,根据determineCurrentLookupKey的返回值来决定
protected DataSource determineTargetDataSource() {
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {