springBoot多數據源配置
配置讀數據源
@Component @ConfigurationProperties(prefix = "jdbc.read") @PropertySource("classpath:application.properties") public class ReadDataSource{ private String userName; private String password; private String driver; private String url; //TODO 此處應有get set方法 }
配置寫數據源
@Component @ConfigurationProperties(prefix = "jdbc.read") @PropertySource("classpath:application.properties") public class WriteDataSource{ private String userName; private String password; private String driver; private String url; //TODO 此處應有get set方法 }
//配置數據源適配器 通過此類的set方法可以動態切換數據源,我們只需出入數據源對應key即可
public class DataSourceHolder { private static final ThreadLocal<String> dataSourceTypes = new ThreadLocal<String>() { @Override protected String initialValue() { return "writeDataSource"; } }; public static String get() { if(StringUtils.isEmpty(dataSourceTypes.get())){ return "writeDataSource"; } return dataSourceTypes.get(); } public static void set(String dataSourceType) { dataSourceTypes.set(dataSourceType); } public static void reset() { dataSourceTypes.set("writeDataSource"); } public static void remove() { dataSourceTypes.remove(); } }
配置多數據源 此處多數據源的動態切換主要就是通過determineCurrentLookupKey獲取對應數據源的key去決定使用哪個數據源
此處需要注意如果處於同一事務中,則數據源不可切換,在事務中,會直接去獲取上一次緩存的數據源,沒有則調用該方法獲取,但只獲取一次,所以有可能會導致數據源切換失敗.后續我們會通過切面去清除緩存數據源.但僅僅是拿到開啟事務第一次獲取的數據源.
public class MultipleDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceHolder.get(); } }
@ConfigurationProperties(prefix = "jdbc.read")此處映射以jdbc.read開頭的配置屬性名和實體類屬性名一致
@PropertySource("classpath:application.properties") 指定從那個屬性配置文件讀取數據源,我的是Maven項目,所以放在resources下
注意:必須要能夠被spring管理起來,所以需要配置到spring掃描路徑.
接下來我們需要一個配置類:配置多數據源
//basePackages 指定讀和寫mapper包位置
@Configuration @MapperScan(basePackages = {"com.xxx.template.dal.mapper.read","com.xxx.template.dal.mapper.write"},sqlSessionTemplateRef = "sqlSessionTemplate") public Class DataSourceConfig{ @AutoWried private ReadDataSource readDataSourceProperties; @AutoWried private ReadDataSource writeDataSourceProperties;
//配置讀數據源屬性 @Bean(destroyMethod = "close") public BasicDataSource readDataSource() { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName(readDataSourceProperties.getDriver()); dataSource.setUrl(readDataSourceProperties.getUrl()); dataSource.setUsername(readDataSourceProperties.getUserName()); dataSource.setPassword(readDataSourceProperties.getPassword()); dataSource.setInitialSize(readDataSourceProperties.getInitialSize()); dataSource.setMaxTotal(readDataSourceProperties.getMaxTotal()); dataSource.setMaxIdle(readDataSourceProperties.getMaxIdle()); dataSource.setRemoveAbandonedOnBorrow(true); dataSource.setRemoveAbandonedTimeout(10); dataSource.setMaxWaitMillis(30000); dataSource.setTestWhileIdle(true); dataSource.setTestOnBorrow(false); dataSource.setTestOnReturn(false); dataSource.setValidationQuery("SELECT 1"); dataSource.setTimeBetweenEvictionRunsMillis(30000); dataSource.setNumTestsPerEvictionRun(30); dataSource.setMinEvictableIdleTimeMillis(600000); return dataSource; } //配置寫數據源屬性 @Bean(destroyMethod = "close") public BasicDataSource writeDataSource() { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName(writeDataSourceProperties.getDriver()); dataSource.setUrl(writeDataSourceProperties.getUrl()); dataSource.setUsername(writeDataSourceProperties.getUserName()); dataSource.setPassword(writeDataSourceProperties.getPassword()); dataSource.setInitialSize(writeDataSourceProperties.getInitialSize()); dataSource.setMaxTotal(writeDataSourceProperties.getMaxTotal()); dataSource.setMaxIdle(writeDataSourceProperties.getMaxIdle()); dataSource.setRemoveAbandonedOnBorrow(true); dataSource.setRemoveAbandonedTimeout(10); dataSource.setMaxWaitMillis(30000); dataSource.setTestWhileIdle(true); dataSource.setTestOnBorrow(false); dataSource.setTestOnReturn(false); dataSource.setValidationQuery("SELECT 1"); dataSource.setTimeBetweenEvictionRunsMillis(30000); dataSource.setNumTestsPerEvictionRun(30); dataSource.setMinEvictableIdleTimeMillis(600000); return dataSource; }
//配置動態數據源屬性 動態數據源包含讀寫數據源
@Bean
public MultipleDataSource dataSource() {
MultipleDataSource multipleDataSource = new MultipleDataSource();
Map<Object, Object> map = new HashMap<>();
map.put("readDataSource",readDataSource());
map.put("writeDataSource" ,writeDataSource());
//此處存放多數據源進入map,根據key動態切換
multipleDataSource.setTargetDataSources(map);
return multipleDataSource;
}
//配置sqlSessionFactory
@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws IOException {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
//指定mapper.xml的位置
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(DataSourceConfig.MAPPER_LOCATION));
//配置mybatis配置的位置
sqlSessionFactoryBean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource(DataSourceConfig.CONFIG_LOCATION));
return sqlSessionFactoryBean;
}
//配置sqlSessionTemplate
@Bean
public SqlSessionTemplate sqlSessionTemplate() throws Exception {
SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory().getObject());
return sqlSessionTemplate;
}
//配置事務管理
@Bean
public DataSourceTransactionManager transactionManager() {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource());
return dataSourceTransactionManager;
} }
此刻我們數據源已經配好,接下來可以手動切換數據源,通過DataSourceHolder 的各種方法獲取,清除,重置.使用完數據源做好調用清除方法,避免緩存導致無法切換數據源
我們也可以指定一個切面類去動態切換數據源
@Aspect @Order(-1) @Component public class DataSourceSwitch { @Before("此處填寫切入點表達式") public void before(){ 切換為讀數據源 DataSourceHolder.set("writeDataSource"); }
@Before("此處填寫切入點表達式") public void before1(){ 切換為讀數據源 DataSourceHolder.set("readDataSource"); } @After("此處填寫切入點表達式") public void after(){ //移除數據源 DataSourceHolder.remove(); } @After("此處填寫切入點表達式") public void after1(){ //移除數據源 DataSourceHolder.remove(); } }
在多數據源和事務結合起來的情況下,無法一個事務下切換數據源,因此只能一個事務下指定一個數據源,比如我們想讀和寫,那么最好使用寫數據源,只讀就只指定讀數據源.
最后在我們方法級別加上@Transactional
在啟動類上加@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})排除spring的默認數據源配置