Spring集成Mybatis多数据源配置

10 篇文章 0 订阅
8 篇文章 0 订阅

既然在整理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);
	}
	
	@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> T unwrap(Class<T> 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<MybatisDataSource> dataSourceTypes = new ThreadLocal<MybatisDataSource>() {
		 
		@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);
	}

}

这个类还是挺好用的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值