利用Spring的AbstractRoutingDataSource来解决多数据源的问题,eg:数据库的读写分离

21 篇文章 1 订阅
2 篇文章 0 订阅
利用Spring的AbstractRoutingDataSource来解决多数据源的问题,eg:数据库的读写分离

步骤:
	1)在配置文件中定义多个datasource。
	2)自定义一个DBContext类,并在其中封装一个静态的ThreadLocal变量(用来存储当前线程应该访问哪个数据库),并实现访问静态ThreadLocal变量的静态方法(设值和取值)。
	3)自定义一个继承了AbstractRoutingDataSource的类DynamicDataSource,并实现父类的determineCurrentLookupKey()方法,该方法从DBContext获取到当前线程应该访问哪个数据库并返回。
	4)在调用dao前,使用切面将dao应该访问的数据库放到DBContext类的dbContext变量中。

	这样,当调用dao时,动态数据源DynamicDataSource就会根据DBContext类的dbContext变量来确定应该使用哪个数据库了。

例子:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop" 
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
	http://www.springframework.org/schema/aop 
	http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
	http://www.springframework.org/schema/tx  
	http://www.springframework.org/schema/tx/spring-tx-3.0.xsd ">

	<description>数据源配置</description>
	
	<!-- 默认数据库 -->
	<bean id="dataSource_advertise" class="org.apache.commons.dbcp.BasicDataSource">		
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="${db.advertise.dbUrl}" />
		<property name="username" value="${db.advertise.userName}" />
		<property name="password" value="${db.advertise.passWord}" />
		<!-- 其它配置 -->
	</bean>
	
	<!-- 只读数据库 -->
	<bean id="dataSource_advertise_read" class="org.apache.commons.dbcp.BasicDataSource">		
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="${db.advertise.read.dbUrl}" />
		<property name="username" value="${db.advertise.read.userName}" />
		<property name="password" value="${db.advertise.read.passWord}" />
		<!-- 其它配置 -->
	</bean>
	
	<!-- 存储百度SEM数据的数据库 -->
	<bean id="dataSource_advertise_baidu" class="org.apache.commons.dbcp.BasicDataSource">		
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="${db.advertise.baidu.dbUrl}" />
		<property name="username" value="${db.advertise.baidu.userName}" />
		<property name="password" value="${db.advertise.baidu.passWord}" />
		<!-- 其它配置 -->
	</bean>
	
	<!-- 存储搜狗SEM数据的数据库 -->
	<bean id="dataSource_advertise_sogou" class="org.apache.commons.dbcp.BasicDataSource">		
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="${db.advertise.sogou.dbUrl}" />
		<property name="username" value="${db.advertise.sogou.userName}" />
		<property name="password" value="${db.advertise.sogou.passWord}" />
		<!-- 其它配置 -->
	</bean>
	

	<!-- 动态数据源,运行时根据key值来选择相应数据库 -->	
	<bean id="dynamicDataSource" class="com.jxn.advertise.datasource.DynamicDataSource">
		<property name="targetDataSources">
			<map key-type="java.lang.String">
				<entry key="dataSource_advertise" value-ref="dataSource_advertise" />
				<entry key="dataSource_advertise_read" value-ref="dataSource_advertise_read" />
				<entry key="dataSource_advertise_baidu" value-ref="dataSource_advertise_baidu" />
				<entry key="dataSource_advertise_sogou" value-ref="dataSource_advertise_sogou" />
			</map>
		</property>
		<property name="defaultTargetDataSource" ref="dataSource_advertise" />
	</bean>

	
	<bean id="dataSourceInterceptor" class="com.jxn.advertise.datasource.DataSourceInterceptor" />
	
	<!-- dao切面配置 -->
	<aop:config>
		<aop:aspect id="dataSourceAspect" ref="dataSourceInterceptor">
			<aop:pointcut id="advertise" expression="execution(* com.jxn.advertise.dao..*.*(..))" />
			<aop:pointcut id="advertise_read" expression="execution(* com.jxn.advertise.read.dao..*.*(..))" />
			<aop:pointcut id="advertise_baidu" expression="execution(* com.jxn.advertise.baidu.dao..*.*(..))" />
			<aop:pointcut id="advertise_sogou" expression="execution(* com.jxn.advertise.sogou.dao..*.*(..))" />
			<aop:before method="setDataSourceAdvertise" pointcut-ref="advertise"/>
			<aop:before method="setDataSourceAdvertiseRead" pointcut-ref="advertise_read"/>
			<aop:before method="setDataSourceAdvertiseBaidu" pointcut-ref="advertise_baidu"/>
			<aop:before method="setDataSourceAdvertiseSogou" pointcut-ref="advertise_sogou"/>
		</aop:aspect>
	</aop:config>
	
	
	<!-- 事务 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dynamicDataSource" />
	</bean>
	
	<tx:annotation-driven transaction-manager="transactionManager" order="50" /> 
	
	
	<!-- 定义SqlSessionFactory -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dynamicDataSource" />
		<property name="configLocation" value="classpath:/mybatis_advertise.xml" />
	</bean>

	
	<!-- MapperScannerConfigurer:自动扫描Mapper接口,为Mapper接口生成代理并注入到Spring中-->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.jxn.advertise.dao,com.jxn.advertise.read.dao,com.jxn.advertise.baidu.dao,com.jxn.advertise.sogou.dao" />
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
	</bean>
	
</beans>

	
自定义的动态数据源DynamicDataSource、自定义的数据库上下文类DBContext、自定义的切面类DataSourceInterceptor:
	
	/**  
     * 动态数据源:根据determineCurrentLookupKey()方法的返回值来确定访问的是哪个数据库
     */  
	import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
	
	public class DynamicDataSource extends AbstractRoutingDataSource {

		@Override
		protected Object determineCurrentLookupKey() {
			return DBContext.getDbContext();
		}

	}


	/**  
     * 通过ThreadLocal模式,提供一个共享环境,存储每个线程将要访问的数据库
     */ 
	public class DBContext {
		private static final ThreadLocal<String> dbContext = new ThreadLocal<String>();

		public static void setDbContext(String dbContextType) {
			dbContext.set(dbContextType);
		}

		public static String getDbContext() {
			return dbContext.get();
		}

		public static void clearDbContext() {
			dbContext.remove();
		}
	}
	
	
	/**  
     * 切面类:在调用DAO方法前,把线程将要访问的数据库存储在共享变量DBContext.dbContext中
     */ 
	public class DataSourceInterceptor {

		public void setDataSourceAdvertise(JoinPoint jp) {
			DBContext.setDbContext("dataSource_advertise");
		}
		
		public void setDataSourceAdvertiseRead(JoinPoint jp) {
			DBContext.setDbContext("dataSource_advertise_read");
		}
		
		public void setDataSourceAdvertiseBaidu(JoinPoint jp) {
			DBContext.setDbContext("dataSource_advertise_baidu");
		}
		
		public void setDataSourceAdvertiseSogou(JoinPoint jp) {
			DBContext.setDbContext("dataSource_advertise_sogou");
		}

	}



		

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值