这两天因为项目扩展,需要用多个数据库来存储不同的数据,为此需要改造为多数据源支持才行,特此记录下

项目的一部分是用spring boot加eureka搭建的,以下以spring boot来说明多数据源配置情况。

1、添加多数据源配置信息

在properties中添加多个不同数据源的配置

spring.datasource.pb.driverClassName = @spring.datasource.pb.driverClassName@
spring.datasource.pb.url=@spring.datasource.pb.url@
spring.datasource.pb.username=@spring.datasource.pb.username@
spring.datasource.pb.password=@spring.datasource.pb.password@

spring.datasource.ct.driverClassName = @spring.datasource.ct.driverClassName@
spring.datasource.ct.url=@spring.datasource.ct.url@
spring.datasource.ct.username=@spring.datasource.ct.username@
spring.datasource.ct.password=@spring.datasource.ct.password@

2、创建数据源类型的常量类

public class DataSourceDialect {
	public static final String ORACLE_PB = "oracle-pb";
	public static final String ORACLE_CT = "oracle-ct"; 
}

3、创建DataSourceConfig配置类,添加配置的多个数据源

@Bean(name = "pbDataSource")
@ConfigurationProperties(prefix = "spring.datasource.pb")
public DataSource pbDataSource() {
	return DataSourceBuilder.create().build();
}

@Bean(name = "ctDataSource")
@ConfigurationProperties(prefix = "spring.datasource.ct")
public DataSource ctDataSource() {
	return DataSourceBuilder.create().build();
}

注:要spring读取自定义配置的数据源,需要先禁止掉spring的自动读取数据源配置类,即在spring boot的项目启动类中加入“@SpringBootApplication(exclude = )”即可

4、重写SpringBoot的数据源配置

@Primary
@Bean("dataSource")
public DynamicDataSource dataSource(@Qualifier("pbDataSource") DataSource pbDataSource,
		@Qualifier("ctDataSource") DataSource ctDataSource) {

	DynamicDataSource dynamicDataSource = new DynamicDataSource();
	Map<Object, Object> targetDataSources = new HashMap<>();
	targetDataSources.put(DataSourceDialect.ORACLE_PB, pbDataSource);
	targetDataSources.put(DataSourceDialect.ORACLE_CT, ctDataSource);
	dynamicDataSource.setTargetDataSources(targetDataSources);
	// 设置默认的数据源
	dynamicDataSource.setDefaultTargetDataSource(ctDataSource);
	return dynamicDataSource;
}

5、根据动态数据源配置,创建sqlSessionFactory

@Primary
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("pbDataSource") DataSource pbDataSource,
		@Qualifier("ctDataSource") DataSource ctDataSource) throws Exception {

	SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
	sessionFactory.setDataSource(this.dataSource(pbDataSource, ctDataSource));
	// sessionFactory.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage"));
	sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
	return sessionFactory.getObject();
}

6、根据动态数据源配置,创建transactionManager

public DataSourceTransactionManager transactionManager(@Qualifier("dataSource") DynamicDataSource dataSource)
		throws Exception {
	return new DataSourceTransactionManager(dataSource);
}

到此动态数据源配置已经完成,那么怎么自由切换为想要的数据源呢。

1、首先定义类DynamicDataSource,它继承了AbstractRoutingDataSource,里面提供了determineCurrentLookupKey方法,该方法可以获取需要使用的datasource的key

public class DynamicDataSource extends AbstractRoutingDataSource{
	@Override
	protected Object determineCurrentLookupKey() {
	}
}

2、那么怎么利用该方法呢,再定义一个数据源的切换类DataSourceSwitch

public class DataSourceSwitch {
	private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

	//设置当前使用的数据源
	public static void setDataSourceType(String dataSourceType) {
		contextHolder.set(dataSourceType);
	}

	//获取当前使用的数据源
	public static String getDataSourceType() {
		System.out.println("使用的数据源:"+contextHolder.get());
		return (String) contextHolder.get();
	}

	// 清除当前数据源
	public static void clearDataSourceType() {
		contextHolder.remove();
	}
}

这里利用了ThreadLocal线程来设置key值,这么做的好处在于能够保护变量被修改,比如同时有两个数据库请求进来,如果用静态变量处理的话,很容易导致第一个线程设置的key被第二个线程给修改了

3、修改DynamicDataSource类的方法

@Override
protected Object determineCurrentLookupKey() {
	return DataSourceSwitch.getDataSourceType();
}

到此多数据源的自由配置即完成了,通过类DataSourceSwitch提供的方法就可以自由切换为想要的数据源,配合aop切面方法还可以更灵活自由的自动搭配