Spring多数据源的配置

标签: spring 数据 | 发表时间:2014-09-12 16:25 | 作者:xinyuan_java
出处:http://www.iteye.com
在大型的应用中,为了提高数据库的水平伸缩性,对多个数据库实例进行管理,需要配置多数据源。在Spring框架被广泛运用的今天,可以很简单的运用Spring中的特性配置动态多数据。

1. 首先配置一个基于c3p0.ComboPooledDataSource的数据源A
daoContext.xml
Xml代码  收藏代码
<bean id="dataSourceA" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> 
    <property name="driverClass" value="${jdbc.driver}"></property> 
    <property name="jdbcUrl" value="${jdbc.url.a}?zeroDateTimeBehavior=convertToNull&amp;characterEncoding=utf8"></property> 
    <property name="user" value="${jdbc.user}"></property> 
    <property name="password" value="${jdbc.password}"></property> 
    <property name="minPoolSize" value="${jdbc.miniPoolSize}" /> 
    <property name="maxPoolSize" value="${jdbc.maxPoolSize}"/>   
    <property name="initialPoolSize" value="${jdbc.initialPoolSize}"/> 
    <property name="maxIdleTime" value="${jdbc.maxIdleTime}"/> 
    <property name="acquireIncrement" value="${jdbc.acquireIncrement}"/> 
    <property name="acquireRetryAttempts" value="${jdbc.acquireRetryAttempts}"/> 
    <property name="acquireRetryDelay" value="${jdbc.acquireRetryDelay}"/> 
    <property name="idleConnectionTestPeriod" value="${jdbc.idleConnectionTestPeriod}"/> 
    <property name="checkoutTimeout" value="${jdbc.checkoutTimeout}"/> 
</bean> 


2. 接着增加一个数据源B
daoContext.xml
Xml代码  收藏代码
<bean id="dataSourceB" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> 
    <property name="driverClass" value="${jdbc.driver}"></property> 
    <property name="jdbcUrl" value="${jdbc.url.b}?zeroDateTimeBehavior=convertToNull&amp;characterEncoding=utf8"></property> 
    <property name="user" value="${jdbc.user}"></property> 
    <property name="password" value="${jdbc.password}"></property> 
    <property name="minPoolSize" value="${jdbc.miniPoolSize}" /> 
    <property name="maxPoolSize" value="${jdbc.maxPoolSize}"/>   
    <property name="initialPoolSize" value="${jdbc.initialPoolSize}"/> 
    <property name="maxIdleTime" value="${jdbc.maxIdleTime}"/> 
    <property name="acquireIncrement" value="${jdbc.acquireIncrement}"/> 
    <property name="acquireRetryAttempts" value="${jdbc.acquireRetryAttempts}"/> 
    <property name="acquireRetryDelay" value="${jdbc.acquireRetryDelay}"/> 
    <property name="idleConnectionTestPeriod" value="${jdbc.idleConnectionTestPeriod}"/> 
    <property name="checkoutTimeout" value="${jdbc.checkoutTimeout}"/> 
</bean> 


3. 接着扩展一个Spring提供的AbstractRoutingDataSource,Override 其中的 determineCurrentLookupKey方法实现数据源的route.
Java代码  收藏代码
package datasource; 
 
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; 
 
public class DynamicDataSource extends AbstractRoutingDataSource{ 
 
    @Override 
    protected Object determineCurrentLookupKey() { 
        return CustomerContextHolder.getCustomerType(); 
    } 


而其中的CustomerContextHolder这是开发人员自己实现的一个封装了ThreadLocal类型的ContextHolder。
Java代码  收藏代码
package datasource; 
 
public class CustomerContextHolder { 
 
    public static final String DATA_SOURCE_A = "dataSourceA"; 
     
    public static final String DATA_SOURCE_B = "dataSourceB"; 
     
    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(); 
    } 


接下来就是在我们上面的daoContext.xml将这个DynamicDataSource Bean加入进去,同时配置targetDataSources的 Map映射。
Xml代码  收藏代码
<bean id="dynamicDataSource" class="datasource.DynamicDataSource" > 
    <!-- 通过key-value的形式来关联数据源 --> 
    <property name="targetDataSources"> 
        <map key-type="java.lang.String"> 
            <entry value-ref="dataSourceA" key="dataSourceA"></entry> 
            <entry value-ref="dataSourceB" key="dataSourceB"></entry> 
        </map> 
    </property> 
    <property name="defaultTargetDataSource" ref="dataSourceA" > 
    </property> 
</bean>  


4. 如何是用这个动态的多数据源呢? 其实很简单,因为我们的DynamicDataSource 是继承与AbstractRoutingDataSource,而AbstractRoutingDataSource又是继承于org.springframework.jdbc.datasource.AbstractDataSource,显然的AbstractDataSource实现了统一的DataSource接口,所以我们的DynamicDataSource 同样可以方便的当一个DataSource使用,下面拿Hibernate做例子:
Xml代码  收藏代码
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> 
    <!-- 可以看到和 普通的dataSource用法一样 --> 
    <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> 

可以看到我们用的仍然是一个sessionFactory,这样看来事务管理的配置也和以前一样。
Xml代码  收藏代码
<tx:annotation-driven transaction-manager="transactionManager"/> 
 
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
    <property name="sessionFactory" ref="sessionFactory" /> 
</bean> 

DynamicDataSource Bean也在容器中了,现在剩下的就在程序中如何控制,选择某一个想要的数据源该怎么做:
Java代码  收藏代码
//这样就将数据源动态的设置成了dataSourceB. 
stomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_B); 

或者使用AOP实现
Java代码  收藏代码
package datasource; 
 
import org.aspectj.lang.JoinPoint; 
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Before; 
import org.aspectj.lang.annotation.Pointcut; 
 
@Aspect 
public class DynamicDataSourceAspect { 
    @Pointcut("execution (public service.impl..*.*(..))") 
    public void serviceExecution(){} 
     
    @Before("serviceExecution()") 
    public void setDynamicDataSource(JoinPoint jp) { 
        for(Object o : jp.getArgs()) { 
            //处理具体的逻辑 ,根据具体的境况CustomerContextHolder.setCustomerType()选取DataSource 
        } 
    } 



5. 总结: 我们可以看到运用AbstractRoutingDataSource可以很好的实现多数据源的,而且以后扩展更多的数据源时也非常容易,只要增加数据源和修改DynamicDataSource Bean的targetDataSources 配置就好。关于选择某一个数据源,其实可以很好的用@Aspect在Service的入口加入一个切面@Pointcut,在@Before里判断JoinPoint的类容选定特定的数据源(例如更加JoinPoint的某个key来判断在设置CustomerContextHolder.setCustomerType)。
根究实际的应用来确定。个人看法: 以前有很多应用部署了writeDataSource和readDataSource和slaveDataBase的实现,但现在的大部分应用在构架上已经不太适合了,越来越注重和用户的交互性使得数据库间他同步变得日益复杂和难以维护,所以在架构系统时不妨考虑使用水平切割的方法来切割数据库,当然这种开发需要需要更多的时间分析业务领域,选取如何的配置数据源其实也是和业务息息相关的。

已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [spring 数据] 推荐:

Spring配置多数据源

- - ITeye博客
首先在配置文件中配置多个dataSource. 扩展Spring的AbstractRoutingDataSource抽象类,实现动态数据源. AbstractRoutingDataSource中的抽象方法determineCurrentLookupKey是实现数据源的route的核心.这里对该方法进行Override.

spring+hibernate多数据源的应用

- - CSDN博客推荐文章
我有两个数据库test,和test1,两个库里都有一张表TEST_ONE. applicationContext.xml的配置如下. //数据库test1配置.   //整合两个数据源,指定数据源管理类.    //数据库test.    //数据库test1. //这个类是用来管理数据源的,配置文件中.

Spring+MyBatis实践——MyBatis访问数据库

- - 开源软件 - ITeye博客
    在http://dufengx201406163237.iteye.com/blog/2102054中描述了工程的配置,在此记录一下如何使用MyBatis访问数据库;. . .

spring实现数据库读写分离

- - 行业应用 - ITeye博客
现在大型的电子商务系统,在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库. Master库负责数据更新和 实时数据查询,Slave库当然负责非实时数据查询. 因为在实际的应用中,数据库都是读多写少(读取数据的频率高,更新数据的频率相对较少),而读取数据 通常耗时比较长,占用数据库服务器的CPU较多,从而影响用户体验.

Spring多数据源的配置

- - 编程语言 - ITeye博客
在大型的应用中,为了提高数据库的水平伸缩性,对多个数据库实例进行管理,需要配置多数据源. 在Spring框架被广泛运用的今天,可以很简单的运用Spring中的特性配置动态多数据. 首先配置一个基于c3p0.ComboPooledDataSource的数据源A. 接着扩展一个Spring提供的AbstractRoutingDataSource,Override 其中的 determineCurrentLookupKey方法实现数据源的route.

Spring Boot使用redis做数据缓存

- - ITeye博客
SysUser.class)); //请注意这里. 3 redis服务器配置. /** *此处的dao操作使用的是spring data jpa,使用@Cacheable可以在任意方法上,*比如@Service或者@Controller的方法上 */ public interface SysUserRepo1 extends CustomRepository {.

Spring MVC防止数据重复提交

- - 编程语言 - ITeye博客
来讲一下如何在Spring MVC里面解决此问题(其它框架也一样,逻辑一样,思想一样,和具体框架没什么关系). 要解决重复提交,有很多办法,比如说在提交完成后redirect一下,也可以用本文提到的使用token的方法(我不使用redirect是因为那样解决不了ajax提交数据或者移动应用提交数据,另一个原因是现在比较通行的方法是使用token,像python里的django框架也是使用token来解决).

spring数据源动态切换

- - 企业架构 - ITeye博客
     原文->http://exceptioneye.iteye.com/blog/1698064.       在Spring 2.0.1中引入了AbstractRoutingDataSource, 该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上.

Spring AOP + Redis缓存数据库查询

- - 编程语言 - ITeye博客
我们希望能够将数据库查询结果缓存到Redis中,这样在第二次做同样的查询时便可以直接从redis取结果,从而减少数据库读写次数. 必须要做到与业务逻辑代码完全分离. 从缓存中读出的数据必须与数据库中的数据一致. 如何为一个数据库查询结果生成一个唯一的标识. Key),能唯一确定一个查询结果,同一个查询结果,一定能映射到同一个.

spring + redis 实现数据的缓存

- - ImportNew
(目的不是加快查询的速度,而是减少数据库的负担). 注意:jdies和commons-pool两个jar的版本是有对应关系的,注意引入jar包是要配对使用,否则将会报错. 因为commons-pooljar的目录根据版本的变化,目录结构会变. 前面的版本是org.apache.pool,而后面的版本是org.apache.pool2….