既然在整理Mybatis那就把經常用的這個多數據源的筆記也整一下吧。
Spring集成Mybatis在之前就已經提到了。Spring集成Mybatis
集成Mybatis多數據源有兩種方式:
1、創建多個SqlSessionFactory,掃描每個SqlSessionFactoryBean對應的包,形成了每個Factory對應一個數據源。
2、創建一個SqlSessionFactory,通過動態切換數據源對象,達到多數據源操作功能。
第一種方式
通過在Spring的配置文件中配置多個SqlSessionFactoryBean對象,每個對應不同的MapperScannerConfigurer,每個MapperScannerConfigurer掃描不同的包路徑接口;
另外一個數據源也如上配置,只需替換對應的掃描包即可,這樣調用指定包下的接口就能訪問指定的數據庫了。
第二種方式
創建單個SqlSessionFactory,指定默認數據源,后期查詢不同的數據庫切換SqlSessionFactory中數據源,如果訪問次數過多,頻繁切換的話,就會導致一個并發問題。
解決這個問題就應該使用并發中一些機制:如果使用鎖機制的話,那么查詢的效率就會降低,同時只有當線程去執行;采用ThreadLocal的話就能解決這個效率以及線程安全的問題了。
由于需切換數據源,所以在創建SqlSessionFactory時需要有幾個注意的點:
1、設置數據源對象應該為一個支持切換的一個DataSource對象,我們先定義為RouteDataSource對象,由于是DataSource所以這個RouteDataSource就必須實現DataSource接口,但是又不能侵入原本數據庫鏈接池的對象,所以這個采用裝飾器模式進行裝飾這個類;
2、支持動態切換,即需要一個暴露的靜態方法進行切換,由于數據源對象都在這個Spring容器當中,所以這個類需拿到Spring的容器使用權(實現ApplicationContextAware接口);
3、需指定切換那個數據源,可以采用ENUM枚舉進行指定,也可以通過String,都可以。
創建一個枚舉類:
public enum DataSourceEnum {DATASOURCE1(null),DATASOURCE2(null);DataSource dataSource;private DataSourceEnum(DataSource dataSource) {this.dataSource = dataSource;}public DataSource getValue() {return dataSource;}public DataSourceEnum setDataSource(DataSource dataSource) {this.dataSource = dataSource;return this;}}
RouteDataSource類如下:
@Component("routeDataSource")
public class RouteDataSource implements DataSource,InitializingBean,ApplicationContextAware {private static final Map<DataSourceEnum,DataSource> targetDataSources = new HashMap<DataSourceEnum,DataSource>(2); //避免并發問題ThreadLocal<DataSource> targetDataSource = new ThreadLocal<DataSource>();//裝時器模式進行數據源增強private static RouteDataSource route = null;public void setDataSource(DataSource targetDataSource) {this.targetDataSource.set(targetDataSource);}@Overridepublic PrintWriter getLogWriter() throws SQLException {return targetDataSource.get().getLogWriter();}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {targetDataSource.get().setLogWriter(out);}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {targetDataSource.get().setLoginTimeout(seconds);}@Overridepublic int getLoginTimeout() throws SQLException {return targetDataSource.get().getLoginTimeout();}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {return targetDataSource.get().getParentLogger();}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {return targetDataSource.get().unwrap(iface);}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return targetDataSource.get().isWrapperFor(iface);}@Overridepublic Connection getConnection() throws SQLException {return targetDataSource.get().getConnection();}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return targetDataSource.get().getConnection(username, password);}//初始化枚舉數據,已經默認數據源@Overridepublic void afterPropertiesSet() throws Exception {targetDataSources.put(DataSourceEnum.DATASOURCE1.setDataSource((DataSource) applicationContext.getBean("dataSource")), (DataSource) applicationContext.getBean("dataSource"));targetDataSources.put(DataSourceEnum.DATASOURCE2.setDataSource((DataSource) applicationContext.getBean("dataSource1")), (DataSource) applicationContext.getBean("dataSource1"));targetDataSource.set(targetDataSources.get(DataSourceEnum.DATASOURCE1));route = (RouteDataSource) applicationContext.getBean("routeDataSource");}private ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}/*** @description 更改數據源方法* @param enumDataSource*/public static void setDataSource(DataSourceEnum enumDataSource) {route.setDataSource(targetDataSources.get(enumDataSource));}}
所以在調用Mybatis的接口之前,調用RouteDataSource.setDataSource(DataSourceEnum.DATASOURCE);即可切換成對應的數據源進行查詢啦。
上面是一個自定義的數據源路由類,后來才發現在Spring的jdbc包下有個支持數據源切換的動態數據源類AbstractRoutingDataSource。
如果使用這個類做數據源切換,也是可以的,實現的思想以及模式都和自定義的那個是一致的;
示例:
public class ThreadLocalRountingDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {// TODO Auto-generated method stub//在這里做數據源切換return DataSourceTypeManager.get();}}
//管理數據源類
public class DataSourceTypeManager {//數據源保存private static final ThreadLocal<MybatisDataSource> dataSourceTypes = new ThreadLocal<MybatisDataSource>() {@Overrideprotected MybatisDataSource initialValue() {return MybatisDataSource.JKDSJ;}};public static MybatisDataSource get() {return dataSourceTypes.get();}public static void set(MybatisDataSource dataSourceType) {dataSourceTypes.set(dataSourceType);}public static void reset() {dataSourceTypes.set(MybatisDataSource.JKDSJ);}}
這個類還是挺好用的