有时候需要在程序中动态切换数据源,那么这个系列的之前的博文所阐述的方法就不再使用了,总不能通过程序更改config.properties文件的dataSource的值,然后再重启web服务器以便加载applicationContext.xml文件。这里讲诉的是如何利用AbstractRoutingDataSource进行数据源动态切换。
首先上applicationContext.xml文件:
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans classpath:/org/springframework/beans/factory/xml/spring-beans-3.0.xsd http://www.springframework.org/schema/aop classpath:/org/springframework/aop/config/spring-aop-3.0.xsd http://www.springframework.org/schema/context classpath:/org/springframework/context/config/spring-context-3.0.xsd http://www.springframework.org/schema/tx classpath:/org/springframework/transaction/config/spring-tx-3.0.xsd"> <!-- IoC配置 --> <!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 --> <context:component-scan base-package="com.shr.dao" /> <context:component-scan base-package="com.shr.service" /> <!-- DAO配置 --> <context:property-placeholder location="classpath:config.properties"/> <bean id="mysql" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${mysql_driver}"/> <property name="url" value="${mysql_url}"/> <property name="username" value="${mysql_username}"/> <property name="password" value="${mysql_password}"/> </bean> <bean id="oracle" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${ora_driver}"/> <property name="url" value="${ora_url}"/> <property name="username" value="${ora_username}"/> <property name="password" value="${ora_password}"/> </bean> <bean id="dataSource" class="com.shr.dao.datasource.DataSources"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry value-ref="mysql" key="MYSQL"></entry> <entry value-ref="oracle" key="ORACLE"></entry> </map> </property> <property name="defaultTargetDataSource" ref="mysql"></property> </bean> <bean id="vendorProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="properties"> <props> <prop key="Oracle">oracle</prop> <prop key="MySQL">mysql</prop> </props> </property> </bean> <bean id="databaseIdProvider" class="org.apache.ibatis.mapping.VendorDatabaseIdProvider"> <property name="properties" ref="vendorProperties" /> </bean> <bean name="myBatisSQLInterceptor" class="com.shr.dao.MyBatisSQLInterceptor"></bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="typeAliasesPackage" value="com.shr.dao.pojo,com.shr.dao.model" /> <property name="databaseIdProvider" ref="databaseIdProvider" /> <property name="mapperLocations"> <list> <value>classpath:com/shr/dao/resources/mappers/*_mapper.xml</value> </list> </property> <!-- <property name="configLocation" value="/WEB-INF/mybatis-config.xml"/> --> <property name="typeHandlersPackage" value="com.shr.dao" /> <property name="plugins"> <list> <ref bean="myBatisSQLInterceptor"/> </list> </property> </bean> <!-- 配置事务管理器 --> <tx:annotation-driven/> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="${dataSource}"/> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.shr.dao.mapper"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <!-- <property name="markerInterface" value="com.shr.dao.mapper.ITemplateMapper"/> --> </bean> </beans>我们可以观察到文件中多了一段:
<bean id="dataSource" class="com.shr.dao.datasource.DataSources"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry value-ref="mysql" key="MYSQL"></entry> <entry value-ref="oracle" key="ORACLE"></entry> </map> </property> <property name="defaultTargetDataSource" ref="mysql"></property> </bean>而且sqlSessionFactory的dataSource是关联到上面这段内容的,而不是通过${dataSource}读取config.properties文件的内容获取的。
这个com.shr.dao.datasource.DataSources是自定义的类,继承自AbstractRoutingDataSource类,实现其determineCurrentLookupKey()方法。
代码如下:
package com.shr.dao.datasource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DataSources extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceSwitch.getDataSourceType(); } }
package com.shr.dao.datasource; public class DataSourceSwitch { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } public static String getDataSourceType() { return contextHolder.get(); } public static void clearDataSourceType() { contextHolder.remove(); } }
package com.shr.dao.datasource; public class DataSourceInstances { public static final String MYSQL="MYSQL"; public static final String ORACLE="ORACLE"; }同样,我们通过一个junit测试用例进行验证:
package com.shr.dao.datasource; import java.util.List; import javax.inject.Inject; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; import com.shr.dao.datasource.DataSourceInstances; import com.shr.dao.datasource.DataSourceSwitch; import com.shr.dao.model.userManage.UserListInfo; import com.shr.service.userManage.UserManageService; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("file:WebContent/WEB-INF/applicationContext.xml") @Transactional @TransactionConfiguration(transactionManager="transactionManager",defaultRollback=false) public class DynamicDataSourceTest { @Inject private UserManageService userManageService; @Test public void test() { DataSourceSwitch.setDataSourceType(DataSourceInstances.MYSQL); List<UserListInfo> list = userManageService.getUserListInfo(); for(UserListInfo user : list) { System.out.println(user.getUser_name()); } } }通过改变DataSourceSwitch.setDataSourceType(DataSourceInstances.ORACLE);可以转换不同的数据源.