既然在整理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 targetDataSources = new HashMap(2);
//避免并发问题
ThreadLocal targetDataSource = new ThreadLocal();
//装时器模式进行数据源增强
private static RouteDataSource route = null;
public void setDataSource(DataSource targetDataSource) {
this.targetDataSource.set(targetDataSource);
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return targetDataSource.get().getLogWriter();
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
targetDataSource.get().setLogWriter(out);
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
targetDataSource.get().setLoginTimeout(seconds);
}
@Override
public int getLoginTimeout() throws SQLException {
return targetDataSource.get().getLoginTimeout();
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return targetDataSource.get().getParentLogger();
}
@Override
public T unwrap(Class iface) throws SQLException {
return targetDataSource.get().unwrap(iface);
}
@Override
public boolean isWrapperFor(Class> iface) throws SQLException {
return targetDataSource.get().isWrapperFor(iface);
}
@Override
public Connection getConnection() throws SQLException {
return targetDataSource.get().getConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return targetDataSource.get().getConnection(username, password);
}
//初始化枚举数据,已经默认数据源
@Override
public 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;
@Override
public 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 {
@Override
protected Object determineCurrentLookupKey() {
// TODO Auto-generated method stub
//在这里做数据源切换
return DataSourceTypeManager.get();
}
}
//管理数据源类
public class DataSourceTypeManager {
//数据源保存
private static final ThreadLocal dataSourceTypes = new ThreadLocal() {
@Override
protected 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);
}
}
这个类还是挺好用的