spring多数据源配置

一、问题提出

项目中我们经常会遇到多数据源的问题,尤其是数据同步或定时任务等项目更是如此。多数据源让人最头痛的,不是配置多个数据源,而是如何能灵活动态的切换数据源。例如在一个spring和hibernate的框架的项目中,我们在spring配置中往往是配置一个dataSource来连接数据库,然后绑定给sessionFactory,在dao层代码中再指定sessionFactory来进行数据库操作。

二、代码实现

1. 首先在配置文件中配置多个dataSource

	<bean id="dataSourceMySql" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="driverClass" value="${mysql.jdbc.driverClass}" />
		<property name="jdbcUrl" value="${mysql.jdbc.url}" />
		<property name="user" value="${mysql.jdbc.user}" />
		<property name="password" value="${mysql.jdbc.password}" />
		<property name="initialPoolSize" value="${mysql.jdbc.initialPoolSize}" />
		<property name="minPoolSize" value="${mysql.jdbc.minPoolSize}" />
		<property name="maxPoolSize" value="${mysql.jdbc.maxPoolSize}" />
		<property name="checkoutTimeout" value="${mysql.jdbc.checkoutTimeout}" />
		<property name="idleConnectionTestPeriod" value="${mysql.jdbc.idleConnectionTestPeriod}" />
		<property name="maxIdleTime" value="${mysql.jdbc.maxIdleTime}" />
		<property name="maxStatements" value="${mysql.jdbc.maxStatements}" />
		<property name="testConnectionOnCheckout" value="${mysql.jdbc.testConnectionOnCheckout}" />
	</bean>

	<bean id="dataSourceOracle" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="driverClass" value="${oracle.jdbc.driverClass}" />
		<property name="jdbcUrl" value="${oracle.jdbc.url}" />
		<property name="user" value="${oracle.jdbc.user}" />
		<property name="password" value="${oracle.jdbc.password}" />
		<property name="initialPoolSize" value="${oracle.jdbc.initialPoolSize}" />
		<property name="minPoolSize" value="${oracle.jdbc.minPoolSize}" />
		<property name="maxPoolSize" value="${oracle.jdbc.maxPoolSize}" />
		<property name="checkoutTimeout" value="${oracle.jdbc.checkoutTimeout}" />
		<property name="idleConnectionTestPeriod" value="${oracle.jdbc.idleConnectionTestPeriod}" />
		<property name="maxIdleTime" value="${oracle.jdbc.maxIdleTime}" />
		<property name="maxStatements" value="${oracle.jdbc.maxStatements}" />
		<property name="testConnectionOnCheckout" value="${oracle.jdbc.testConnectionOnCheckout}" />
	</bean>

2. 扩展Spring的AbstractRoutingDataSource抽象类,实现动态数据源。
AbstractRoutingDataSource中的抽象方法determineCurrentLookupKey是实现数据源的route的核心.这里对该方法进行Override。

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {

	@Override
	protected Object determineCurrentLookupKey() {
		return DatabaseContextHolder.getCustomerType();
	}

}

上下文DatabaseContextHolder为一线程安全的ThreadLocal,具体代码如下:

public class DatabaseContextHolder {

	private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

	public static void setCustomerType(String customerType) {
		contextHolder.set(customerType);
	}

	public static String getCustomerType() {
		return contextHolder.get();
	}

	public static void clearCustomerType() {
		contextHolder.remove();
	}
}

3. 配置动态数据源
将DynamicDataSource Bean加入到Spring的上下文xml配置文件中去,同时配置DynamicDataSource的targetDataSources(多数据源目标)属性的Map映射。

	<bean id="dataSource" class="com.core.DynamicDataSource">
		<property name="targetDataSources">
			<map key-type="java.lang.String">
				<entry key="dataSourceMySql" value-ref="dataSourceMySql" />
				<entry key="dataSourceOracle" value-ref="dataSourceOracle" />
			</map>
		</property>
		<property name="defaultTargetDataSource" ref="dataSourceMySql" />
	</bean>

4. 使用动态数据源
DynamicDataSource是继承与AbstractRoutingDataSource,而AbstractRoutingDataSource又是继承于org.springframework.jdbc.datasource.AbstractDataSource,AbstractDataSource实现了统一的DataSource接口,所以DynamicDataSource同样可以当一个DataSource使用。

<!-- JdbcTemplate使用动态数据源的配置 -->     
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">     
    <property name="dataSource">     
        <ref bean="dataSource" />     
    </property>     
</bean>     
     
<!-- 对JdbcTemplate的应用封装类 -->     
<bean id="sqlBaseDAO" class="com.whty.dao.BaseDAOImpl">     
    <property name="jdbcTemplate">     
        <ref bean="jdbcTemplate" />     
    </property>     
</bean>  
5. 动态数据源的管理
如何选择控制每个业务中需要的具体数据源,可是使用手动控制:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BaseDAO dao = (BaseDAO) context.getBean("baseDAO", BaseDAOImpl.class);

try {
    DatabaseContextHolder.setCustomerType("dataSourceMySql");
    System.err.println(dao.select("select count(*) sum from TEST t ").get(0).get("SUM"));
    DatabaseContextHolder.setCustomerType("dataSourceOracle");
    System.err.println(dao.select("select count(*) sum from TEST t ").get(0).get("SUM"));
} catch (Exception e) {
    e.printStackTrace();
}
如果在service层有比较统一的规则的话,也可以使用aop设置数据源使用。

	<bean id="dataSourceInterceptor" class="com.core.DataSourceInterceptor" />

	<aop:config>
		<aop:aspect id="dataSourceAspect" ref="dataSourceInterceptor">
			<aop:pointcut id="dsMysql" expression="execution(* com.controller.mysql.*.*(..))" />
			<aop:pointcut id="dsOracle" expression="execution(* com.controller.oracle.*.*(..))" />
			<aop:before method="setdataSourceMysql" pointcut-ref="dsMysql"/>
			<aop:before method="setdataSourceOracle" pointcut-ref="dsOracle"/>
		</aop:aspect>
	</aop:config>
import org.aspectj.lang.JoinPoint;

public class DataSourceInterceptor {

	public void setdataSourceMysql(JoinPoint jp) {
		DatabaseContextHolder.setCustomerType("dataSourceMySql");
	}
	
	public void setdataSourceOracle(JoinPoint jp) {
		DatabaseContextHolder.setCustomerType("dataSourceOracle");
	}
}



这里一般都是一个service一个数据源,所以最好使用aop在service层执行完之后统一调用
DBContextHolder.clearDBType();  
清空数据源信息。

三、注意事项

1. 注意事务拦截器的配置
这是首要的一条。首先你要明白,Spring的事务管理是与数据源绑定的,一旦程序执行到事务管理的那一层(如service)的话,由于在进入该层之前事务已经通过拦截器开启,因此在该层切换数据源是不行的,明白事务的原理是尤为重要的,我之前的文章中,将切换数据源的拦截器配置在了Dao层是有问题的(因为是示例,所以粗心了,对误导了大家我表示道歉),但提供的思路是没有问题的。
Demo中将切换数据源的拦截器(dataSourceInterceptor)配置在了事务拦截器(txadvice)的上一层,也就是Controller层。
2. 注意数据库表的创建
一些人喜欢用Hibernate的自动创建表的功能,但需要注意,在多数据源中,尤其是不同数据库的多数据源,想都自动建表是不行的。因为Hibernate自动建表是在项目启动时触发的,因此只会建立项目配置的默认数据源的表,而其他数据源的表则不会自动创建。大家要注意着点。
3. Hibernate的数据库方言(dialect)可以忽略
在多数据源时,方言的设置可以忽略,Hibernate在使用时会自动识别不同的数据库,因此不必纠结这个配置,甚至不配置也可以。
4. 报No current session错误
这个是因为使用了sessionFactory.getCurrentSession()导致的,current session是与线程绑定的,一个线程只会开启一个Session(除非使用openSession()就不会报错),因此需要设置session与线程的绑定关系。
Demo中使用了Spring管理Hibernate的session,因此在web.xml中配置了OpenSessionInViewFilter,并在hibernate.cfg.xml中配置了current_session_context_class。【PS:使用Spring管理Hibernate时,可以去掉hibernate.cfg.xml,而全部配置的Spring的配置文件里,即hibernateProperties。看个人喜好吧】

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值