利用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");
}
}
利用Spring的AbstractRoutingDataSource来解决多数据源的问题,eg:数据库的读写分离
猜你喜欢
转载自blog.csdn.net/A__17/article/details/73800680
今日推荐
周排行