Spring多數(shù)據(jù)源的配置和使用
最近開發(fā)一個數(shù)據(jù)同步的小功能,需要從A主機的Oracle數(shù)據(jù)庫中把數(shù)據(jù)同步到B主機的Oracle庫中,。當然能夠用dmp腳本或者SQL腳本是最
好,,但是對于兩邊異構(gòu)的表結(jié)構(gòu)來說,,直接導(dǎo)入不可行,。然后在需要實時同步的情況下用存儲過程也不可行了。寫一個數(shù)據(jù)同步的小程序是個不錯的選擇,。使用框架
的封裝和連接池是必須的,Spring是首選,,這里我們同樣需要Spring的多數(shù)據(jù)源連接配置方式,。
其實再進行項目開發(fā)的時候,,一個項目有可能不止用到一個數(shù)據(jù)源,為了提高數(shù)據(jù)庫的水平伸縮性,,需要對多個數(shù)據(jù)庫實例進行管理,,需要配置多數(shù)據(jù)源。
1. 配置多個數(shù)據(jù)源
這里以兩個c3p0數(shù)據(jù)庫連接池的數(shù)據(jù)源作為實例,。在Spring框架下使用c3p0的數(shù)據(jù)庫需要加入c3p0-0.9.1.2.jar(現(xiàn)在最新的)這個支持包,。這里以數(shù)據(jù)同步項目為例:
數(shù)據(jù)來源庫的連接池數(shù)據(jù)源配置:
- <bean id="dataSourceFrom" class="com.mchange.v2.c3p0.ComboPooledDataSource">
- <property name="driverClass" value="${jdbc.driver}" />
- <property name="jdbcUrl" value="${jdbc.from.url}" />
- <property name="user" value="${jdbc.from.username}" />
- <property name="password" value="${jdbc.from.password}" />
- <property name="autoCommitOnClose" value="true" />
- <property name="checkoutTimeout" value="${cpool.checkoutTimeout}" />
- <property name="initialPoolSize" value="${cpool.minPoolSize}" />
- <property name="minPoolSize" value="${cpool.minPoolSize}" />
- <property name="maxPoolSize" value="${cpool.maxPoolSize}" />
- <property name="maxIdleTime" value="${cpool.maxIdleTime}" />
- <property name="acquireIncrement" value="${cpool.acquireIncrement}" />
- <property name="maxIdleTimeExcessConnections" value="${cpool.maxIdleTimeExcessConnections}" />
- </bean>
數(shù)據(jù)插入庫的連接池數(shù)據(jù)源配置:
- <bean id="dataSourceTo" class="com.mchange.v2.c3p0.ComboPooledDataSource">
- <property name="driverClass" value="${jdbc.driver}" />
- <property name="jdbcUrl" value="${jdbc.to.url}" />
- <property name="user" value="${jdbc.to.username}" />
- <property name="password" value="${jdbc.to.password}" />
- <property name="autoCommitOnClose" value="true" />
- <property name="checkoutTimeout" value="${cpool.checkoutTimeout}" />
- <property name="initialPoolSize" value="${cpool.minPoolSize}" />
- <property name="minPoolSize" value="${cpool.minPoolSize}" />
- <property name="maxPoolSize" value="${cpool.maxPoolSize}" />
- <property name="maxIdleTime" value="${cpool.maxIdleTime}" />
- <property name="acquireIncrement" value="${cpool.acquireIncrement}" />
- <property name="maxIdleTimeExcessConnections" value="${cpool.maxIdleTimeExcessConnections}" />
- </bean>
注意:上面url,user,,password等值是從classpath下的jdbc.properties中取得的,。
通過Spring獲取屬性文件中的值,以供配置文件使用:
- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
- <property name="locations" value="classpath:jdbc.properties" />
- </bean>
2. 擴展Spring的AbstractRoutingDataSource抽象類,,實現(xiàn)動態(tài)數(shù)據(jù)源,。
AbstractRoutingDataSource中的抽象方法determineCurrentLookupKey是實現(xiàn)數(shù)據(jù)源的route的核心.這里對該方法進行Override。
- public class DynamicDataSource extends AbstractRoutingDataSource{
-
- @Override
- protected Object determineCurrentLookupKey() {
- return DBContextHolder.getDBType();
- }
- }
上下文DbContextHolder為一線程安全的ThreadLocal,,具體代碼如下:
- public class DBContextHolder{
- public static final String DATA_SOURCE_FROM = "dataSourceFrom";
- public static final String DATA_SOURCE_TO = "dataSourceTo";
-
- private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
-
- public static void setDBType(String dbType) {
- contextHolder.set(dbType);
- }
-
- public static String getDBType() {
- return contextHolder.get();
- }
-
- public static void clearDBType() {
- contextHolder.remove();
- }
- }
3.配置動態(tài)數(shù)據(jù)源
將DynamicDataSource Bean加入到Spring的上下文xml配置文件中去,同時配置DynamicDataSource的targetDataSources(多數(shù)據(jù)源目標)屬性的Map映射,。
- <bean id="dynamicDataSource" class="datasource.DynamicDataSource" >
-
- <property name="targetDataSources">
- <map>
- <entry value-ref="dataSourceFrom" key="dataSourceFrom"></entry>
- <entry value-ref="dataSourceTo" key="dataSourceTo"></entry>
- </map>
- </property>
- <property name="defaultTargetDataSource" ref="dataSourceFrom" />
- </bean>
4.使用動態(tài)數(shù)據(jù)源
例子中DynamicDataSource是繼承與AbstractRoutingDataSource,,而
AbstractRoutingDataSource又是繼承于
org.springframework.jdbc.datasource.AbstractDataSource,AbstractDataSource
實現(xiàn)了統(tǒng)一的DataSource接口,,所以DynamicDataSource同樣可以當一個DataSource使用,。
在Spring的JdbcTemplate使用動態(tài)數(shù)據(jù)源的配置示例:
-
- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
- <property name="dataSource">
- <ref bean="dynamicDataSource" />
- </property>
- </bean>
-
-
- <bean id="sqlBaseDAO" class="com.whty.dao.BaseDAOImpl">
- <property name="jdbcTemplate">
- <ref bean="jdbcTemplate" />
- </property>
- </bean>
在ORM框架Hibernate中的使用配置示例:
- <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
-
- <property name="dataSource" ref="dynamicDataSource" />
- <property name="configLocations" value="classpath:hibernate.cfg.xml" />
- <property name="hibernateProperties">
- <props>
- <prop key="hibernate.dialect">${hibernate.dialect}</prop>
- </property>
- </bean>
5.事務(wù)管理
使用動態(tài)數(shù)據(jù)源的時候,可以看出和使用單數(shù)據(jù)源的時候相比,,在使用配置上幾乎沒有差別,,在進行性事務(wù)管理配置的時候也沒有差別:
使用Spring的JdbcTemplate的事務(wù)管理配置示例:
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dynamicDataSource" />
- </bean>
-
- <bean id="sqlBaseDAOProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
- <property name="transactionManager" ref="transactionManager" />
- <property name="target" ref="sqlBaseDAO" />
- <property name="transactionAttributes">
- <props>
- <prop key="insert*">PROPAGATION_REQUIRED</prop>
- <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
- </props>
- </property>
- </bean>
使用Hibernate時的事務(wù)管理配置示例:
- <tx:annotation-driven transaction-manager="transactionManager"/>
-
- <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
- <property name="sessionFactory" ref="sessionFactory" />
- </bean>
6.動態(tài)數(shù)據(jù)源的管理控制
如何選擇控制每個業(yè)務(wù)中需要的具體數(shù)據(jù)源,可是使用手動控制:
- ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- BaseDAO dao = (BaseDAO) context.getBean("sqlBaseDAO", BaseDAOImpl.class);
-
- try {
- DBContextHolder.setCustomerType(DBContextHolder.DATA_SOURCE_FROM);
- System.err.println(dao.select("select count(*) sum from TEST t ").get(0).get("SUM"));
- DBContextHolder.setCustomerType(DBContextHolder.DATA_SOURCE_TO);
- System.err.println(dao.select("select count(*) sum from TEST t ").get(0).get("SUM"));
-
- } catch (Exception e) {
- e.printStackTrace();
- }
也可以采用AOP的控制方式:
- @Aspect
- public class DynamicDataSourceAspect {
- @Pointcut("execution (public service.impl..*.*(..))")
- public void serviceExecution(){}
-
- @Before("serviceExecution()")
- public void setDynamicDataSource(JoinPoint jp) {
- for(Object o : jp.getArgs()) {
-
- }
- }
- }
7.總結(jié)
通過擴展Spring的AbstractRoutingDataSource可以很好的實現(xiàn)多數(shù)據(jù)源的rout效果,,而且對擴展更多的數(shù)據(jù)源有良好的伸縮
性,只要增加數(shù)據(jù)源和修改DynamicDataSource的targetDataSources屬性配置就好,。在數(shù)據(jù)源選擇控制上,,可以采用手動控制
(業(yè)務(wù)邏輯并不多的時候),也可以很好的用AOP的@Aspect在Service的入口加入一個切面@Pointcut,在@Before里判斷
JoinPoint的類容選定特定的數(shù)據(jù)源,。
|