转载之:http://exceptioneye.iteye.com/blog/1698064
Spring配置多数据源的方式和具体使用过程
1、数据源的名称常量类
public enum DynamicDataSourceGlobal {
- ORCL,
- ISC
- }
2、建立一个获得和设置上下文环境的类,主要负责改变上下文数据源的名称
public class DynamicDataSourceHolder {
- // 线程本地环境
- private static final ThreadLocal<DynamicDataSourceGlobal> contextHolder = new ThreadLocal<DynamicDataSourceGlobal>();
- // 设置数据源类型
- public static void setDataSourceType(DynamicDataSourceGlobal dataSourceType) {
- Assert.notNull(dataSourceType, "DataSourceType cannot be null");
- contextHolder.set(dataSourceType);
- }
- // 获取数据源类型
- public static DynamicDataSourceGlobal getDataSourceType() {
- return (DynamicDataSourceGlobal) contextHolder.get();
- }
- // 清除数据源类型
- public static void clearDataSourceType() {
- contextHolder.remove();
- }
3、建立动态数据源类,注意,这个类必须继承AbstractRoutingDataSource,且实现方法 determineCurrentLookupKey,该方法返回一个Object,一般是返回字符串
public class DynamicDataSource extends AbstractRoutingDataSource {
- @Override
- protected Object determineCurrentLookupKey() {
- return DynamicDataSourceHolder.getDataSourceType();
- }
- }
4、编写spring的配置文件配置多个数据源
<!-- 数据源相同的内容 -->
- <bean id="parentDataSource"
- class="org.springframework.jdbc.datasource.DriverManagerDataSource">
- <property name="driverClass"
- value="oracle.jdbc.pool.OracleConnectionPoolDataSource" />
- <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl" />
- <property name="user" value="isc_v10" />
- <property name="password" value="isc" />
- </bean>
- <!-- 数据源 -->
- <bean id="orclDataSource" parent="parentDataSource">
- <property name="user" value="orcl" />
- <property name="password" value="orcl" />
- </bean>
- <!-- 数据源 -->
- <bean id="iscDataSource" parent="parentDataSource">
- <property name="user" value="isc_v10" />
- <property name="password" value="isc" />
- </bean>
- <!-- 编写spring 配置文件的配置多数源映射关系 -->
- <bean id="dataSource" class="com.wy.config.DynamicDataSource">
- <property name="targetDataSources">
- <map key-type="java.lang.String">
- <entry key="ORCL" value-ref="orclDataSource"></entry>
- <entry key="ISC" value-ref="iscDataSource"></entry>
- </map>
- </property>
- <property name="defaultTargetDataSource" ref="orclDataSource">
- </property>
- </bean>
- <bean id="sessionFactory"
- class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
- <property name="dataSource" ref="dataSource" />
- </bean>
5、使用
@Test
- public void testSave() {
- // hibernate创建实体
- DynamicDataSourceHolder.setDataSourceType(DynamicDataSourceGlobal.ORCL);// 设置为另一个数据源
- com.wy.domain.Test user = new com.wy.domain.Test();
- user.setName("WY");
- user.setAddress("BJ");
- testDao.save(user);// 使用dao保存实体
- DynamicDataSourceHolder.setDataSourceType(DynamicDataSourceGlobal.ISC);// 设置为另一个数据源
- testDao.save(user);// 使用dao保存实体到另一个库中
- }
用spring 的AbstractRoutingDataSource解决了这个问题。
原理如图:
项目采用的是hibernate,直接在spring.xml设置sessionFactory的dataSource属性为动态数据源即可。
因为项目所有数据库结构都一致,为了避免每次设置数据源的时候要改一堆参数,修改了springAbstractRoutingDataSource类增加了一个getTargetDataSources方法,获取当前数据源详细信息,在其基础上修改数据库名称、用户名、密码即可,不用每次设置一堆参数。
- Map<String, ComboPooledDataSource> targetDataSources = dynamicDataSource
- .getTargetDataSources();
- if (targetDataSources == null) {
- targetDataSources = new HashMap<String, ComboPooledDataSource>();
- targetDataSources.put("baseDataSource", baseDataSource);
- }
- targetDataSources.put(dataSourceName, subSystemDataSource);
- dynamicDataSource.setTargetDataSources(targetDataSources);
- dynamicDataSource.afterPropertiesSet();
另外,设置AbstractRoutingDataSource参数后要调用afterPropertiesSet()方法,spring容器才会进行加载操作。
在动态设置数据源方面,可以通过两种方式实现:
- 在action(项目使用struts)中进行设置,可以确保在每个servlet线程中数据源是一致的。
- 以aop方式,对service方法进行拦截,根据需求设置不同数据源。