在 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對應的方法上面設置注解,就可以很靈活的實現不同的方法調用不同的數據源。
