在 springboot mybatis plus多数据源轻松搞定 (上) 中我们使用了分包的方式实现了一个springboot项目中多个数据源的调用。也对指出了最大的缺点就是不能灵活自由的切换数据源。那么这一篇中,我们探讨一下动态的实现多数据源的方式。可以实现随心所欲的切换数据源。
基础的配置
- 数据源的yml配置和上一结一样,就不在赘述了。
- 建立一个枚举类来标识两个数据源
public enum DataSourceType {
emanage,ehr
}
建立一个线程和数据源之间的关联类
public class DataBaseContextHolder {
private static final ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<>();
public static void setDataSourceType(DataSourceType type)
{
if(type == null)
{
throw new NullPointerException();
}
contextHolder.set(type);
}
public static DataSourceType getDataSourceType()
{
DataSourceType type = contextHolder.get();
if(type == null)
{
//确定一个默认数据源
return DataSourceType.emanage;
}
return type;
}
public static void clearDataSrouceType()
{
contextHolder.remove();
}
}
代码比较简单。就是当我们设置一个Mapper是通过那个数据源去访问数据的时候,把设置的参数保存在contextHolder中,为了处理线程安全,采用ThreadLocal的方式。
定义动态数据源
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataBaseContextHolder.getDataSourceType();
}
}
定义多数据源
@Configuration
@MapperScan("com.emanage.ehr.mapper")
public class DataSourceConfig {
@Autowired
private Environment env;
@Bean(name = "datasource1")
@ConfigurationProperties(prefix = "spring.datasource.emanage")
public DruidDataSource druidDataSource1()
{
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "datasource2")
@ConfigurationProperties(prefix = "spring.datasource.ehr")
public DruidDataSource druidDataSource2()
{
return DruidDataSourceBuilder.create().build();
}
@Bean
public DynamicDataSource dynamicDataSource(@Qualifier("datasource1") DruidDataSource ds1,
@Qualifier("datasource2") DruidDataSource ds2)
{
Map<Object, Object> targetDataSource = new HashMap<>();
targetDataSource.put(DataSourceType.emanage, ds1);
targetDataSource.put(DataSourceType.ehr, ds2);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSource);
dataSource.setDefaultTargetDataSource(ds1);
return dataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
// 指定数据源
bean.setDataSource(dynamicDataSource);
bean.setMapperLocations(resolver.getResources("classpath*:mapper/**Mapper.xml"));
return bean.getObject();
}
@Bean
public DataSourceTransactionManager transactionManager(DynamicDataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}
}
使用数据源
在调用mapper
之前,在service
中执行以下代码,可以灵活的切换数据源。
DataBaseContextHolder.setDataSourceType(DataSourceType.emanage);
DataBaseContextHolder.setDataSourceType(DataSourceType.ehr);
优化升级
感觉在sevrice中调用这些代码太过繁琐,可以自己定义两个注解。
public @interface DataSourceEmanage{}
public @interface DataSourceEHr{}
然后建立一个aop类让在有些注解的mapper类执行之前,先执行相应的数据源切换。
@Aspect
@Component
public class DataSourceAop {
@Before("@annotation(com.example.demo3.config.DataSourceEmanage)")
public void setEmanageDataSource()
{
DataBaseContextHolder.setDataSourceType(DataSourceType.emanage);
}
@Before("@annotation(com.example.demo3.config.DataSourceEhr)")
public void setEhrDataSource()
{
DataBaseContextHolder.setDataSourceType(DataSourceType.ehr);
}
}
只需要在mapper对应的方法上面设置注解,就可以很灵活的实现不同的方法调用不同的数据源。