既然在整理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);}}
这个类还是挺好用的