Spring多數據源配置


在大型的應用中,為了提高數據庫的水平伸縮性,對多個數據庫實例進行管理,需要配置多數據源。在Spring框架被廣泛運用的今天,可以很簡單的運用Spring中的特性配置動態多數據。 

1. 首先配置一個基於c3p0.ComboPooledDataSource的數據源A,數據源B.

daoContext.xml 

[html]  view plain copy
  1. <bean id="dataSourceA" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  
  2.     <property name="driverClass" value="${jdbc.driver}"></property>  
  3.     <property name="jdbcUrl" value="${jdbc.ur.al}?zeroDateTimeBehavior=convertToNull&characterEncoding=utf8"></property>  
  4.     <property name="user" value="${jdbc.user}"></property>  
  5.     <property name="password" value="${jdbc.password}"></property>  
  6.     <property name="minPoolSize" value="${jdbc.miniPoolSize}" />  
  7.     <property name="maxPoolSize" value="${jdbc.maxPoolSize}"/>    
  8.     <property name="initialPoolSize" value="${jdbc.initialPoolSize}"/>  
  9.     <property name="maxIdleTime" value="${jdbc.maxIdleTime}"/>  
  10.     <property name="acquireIncrement" value="${jdbc.acquireIncrement}"/>  
  11.     <property name="acquireRetryAttempts" value="${jdbc.acquireRetryAttempts}"/>  
  12.     <property name="acquireRetryDelay" value="${jdbc.acquireRetryDelay}"/>  
  13.     <property name="idleConnectionTestPeriod" value="${jdbc.idleConnectionTestPeriod}"/>  
  14.     <property name="checkoutTimeout" value="${jdbc.checkoutTimeout}"/>  
  15. </bean>  
  16.   
  17. <bean id="dataSourceB" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  
  18.     <property name="driverClass" value="${jdbc.driver}"></property>  
  19.     <property name="jdbcUrl" value="${jdbc.url.b}?zeroDateTimeBehavior=convertToNull&characterEncoding=utf8"></property>  
  20.     <property name="user" value="${jdbc.user}"></property>  
  21.     <property name="password" value="${jdbc.password}"></property>  
  22.     <property name="minPoolSize" value="${jdbc.miniPoolSize}" />  
  23.     <property name="maxPoolSize" value="${jdbc.maxPoolSize}"/>    
  24.     <property name="initialPoolSize" value="${jdbc.initialPoolSize}"/>  
  25.     <property name="maxIdleTime" value="${jdbc.maxIdleTime}"/>  
  26.     <property name="acquireIncrement" value="${jdbc.acquireIncrement}"/>  
  27.     <property name="acquireRetryAttempts" value="${jdbc.acquireRetryAttempts}"/>  
  28.     <property name="acquireRetryDelay" value="${jdbc.acquireRetryDelay}"/>  
  29.     <property name="idleConnectionTestPeriod" value="${jdbc.idleConnectionTestPeriod}"/>  
  30.     <property name="checkoutTimeout" value="${jdbc.checkoutTimeout}"/>  
  31. </bean>  
3. 接着擴展一個Spring提供的AbstractRoutingDataSource,Override 其中的 determineCurrentLookupKey方法實現數據源的route. 

[java]  view plain copy
  1. package datasource;  
  2.   
  3. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;  
  4.   
  5. public class DynamicDataSource extends AbstractRoutingDataSource{  
  6.   
  7.     @Override  
  8.     protected Object determineCurrentLookupKey() {  
  9.         return CustomerContextHolder.getCustomerType();  
  10.     }  
  11. }  
而其中的CustomerContextHolder這是開發人員自己實現的一個封裝了ThreadLocal類型的ContextHolder。

[java]  view plain copy
  1. package datasource;  
  2.   
  3. public class CustomerContextHolder {  
  4.   
  5.     public static final String DATA_SOURCE_A = "dataSourceA";  
  6.       
  7.     public static final String DATA_SOURCE_B = "dataSourceB";  
  8.       
  9.     private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();  
  10.       
  11.     public static void setCustomerType(String customerType) {  
  12.         contextHolder.set(customerType);  
  13.     }  
  14.       
  15.     public static String getCustomerType() {  
  16.         return contextHolder.get();  
  17.     }  
  18.       
  19.     public static void clearCustomerType() {  
  20.         contextHolder.remove();  
  21.     }  
  22. }  
4.接下來就是在我們上面的daoContext.xml將這個DynamicDataSource Bean加入進去,同時配置targetDataSources的 Map映射。 

[html]  view plain copy
  1. <bean id="dynamicDataSource" class="datasource.DynamicDataSource" >  
  2.     <!-- 通過key-value的形式來關聯數據源 -->  
  3.     <property name="targetDataSources">  
  4.         <map key-type="java.lang.String">  
  5.             <entry value-ref="dataSourceA" key="dataSourceA"></entry>  
  6.             <entry value-ref="dataSourceB" key="dataSourceB"></entry>  
  7.         </map>  
  8.     </property>  
  9.     <property name="defaultTargetDataSource" ref="dataSourceA" >  
  10.     </property>  
  11. </bean>   
5. 如何是用這個動態的多數據源呢? 其實很簡單,因為我們的DynamicDataSource 是繼承與AbstractRoutingDataSource,而AbstractRoutingDataSource又是繼承於org.springframework.jdbc.datasource.AbstractDataSource,顯然的AbstractDataSource實現了統一的DataSource接口,所以我們的DynamicDataSource 同樣可以方便的當一個DataSource使用,下面拿Hibernate做例子: 

[html]  view plain copy
  1.   
[java]  view plain copy
  1. <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">  
  2.      <!-- 可以看到和 普通的dataSource用法一樣 -->  
  3.     <property name="dataSource" ref="dynamicDataSource" />  
  4.     <property name="configLocations" value="classpath:hibernate.cfg.xml" />  
  5.     <property name="hibernateProperties">  
  6.          <props>  
  7.                 <prop key="hibernate.dialect">${hibernate.dialect}</prop>  
  8.          </props>   
  9.     </property>  
  10. </bean>  
可以看到我們用的仍然是一個sessionFactory,這樣看來事務管理的配置也和以前一樣。

[html]  view plain copy
  1. <tx:annotation-driven transaction-manager="transactionManager"/>  
  2.   
  3. <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
  4.     <property name="sessionFactory" ref="sessionFactory" />  
  5. </bean>  

DynamicDataSource Bean也在容器中了,現在剩下的就在程序中如何控制,選擇某一個想要的數據源該怎么做:

[java]  view plain copy
  1. //這樣就將數據源動態的設置成了dataSourceB.  
  2. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_B);  
或者使用AOP實現 
[java]  view plain copy
  1. package datasource;  
  2.   
  3. import org.aspectj.lang.JoinPoint;  
  4. import org.aspectj.lang.annotation.Aspect;  
  5. import org.aspectj.lang.annotation.Before;  
  6. import org.aspectj.lang.annotation.Pointcut;  
  7.   
  8. @Aspect  
  9. public class DynamicDataSourceAspect {  
  10.     @Pointcut("execution (public service.impl..*.*(..))")  
  11.     public void serviceExecution(){}  
  12.       
  13.     @Before("serviceExecution()")  
  14.     public void setDynamicDataSource(JoinPoint jp) {  
  15.         for(Object o : jp.getArgs()) {  
  16.             //處理具體的邏輯 ,根據具體的境況CustomerContextHolder.setCustomerType()選取DataSource  
  17.         }  
  18.     }  
  19. }  
[plain]  view plain copy
  1. 6. 總結: 我們可以看到運用AbstractRoutingDataSource可以很好的實現多數據源的,而且以后擴展更多的數據源時也非常容易,只要增加數據源和修改DynamicDataSource Bean的targetDataSources 配置就好。關於選擇某一個數據源,其實可以很好的用@Aspect在Service的入口加入一個切面@Pointcut,在@Before里判斷JoinPoint的類容選定特定的數據源(例如更加JoinPoint的某個key來判斷在設置CustomerContextHolder.setCustomerType)。   
  2. 根究實際的應用來確定。個人看法: 以前有很多應用部署了writeDataSource和readDataSource和slaveDataBase的實現,但現在的大部分應用在構架上已經不太適合了,越來越注重和用戶的交互性使得數據庫間他同步變得日益復雜和難以維護,所以在架構系統時不妨考慮使用水平切割的方法來切割數據庫,當然這種開發需要需要更多的時間分析業務領域,選取如何的配置數據源其實也是和業務息息相關的。   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM