spring+mybaties 配置多数据源



在实际的开发中遇到了需要2个数据源的情况。这里我们就需要配置多数据源了。
1  首先配置多数据源有2种实现方式   
1)简单方式:一般情况下,我们可以配置多个数据源,然后为每个数据源写一套对应的sessionFactory和dao层代码我们称之为静态数据源配置
2)动态方式: 配置多个数据源,只对应一套sessionFactory,数据源之间可以动态切换。</span>
2   這两种方式相比较来看,第一种如果以后要配置多个数据源的话,对应都要配置sessionfactory. 第2种只需配置多个数据源自由切换对应一个sessionfactory 明显比第一个更装B一点。下面就是详细介绍我自己配置的第2中方法。首先是先配置2个数据源




<pre name="code" class="html"><beans xmlns="http://www.springframework.org/schema/beans"
 
	xmlns:aop="http://www.springframework.org/schema/aop" 
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
	http://www.springframework.org/schema/aop 
	http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
	http://www.springframework.org/schema/tx  
	http://www.springframework.org/schema/tx/spring-tx-3.0.xsd ">

	<description>公共的默认数据源配置</description>
		
	<!-- portal master数据库 -->
	<bean id="dataSource_master" class="org.apache.commons.dbcp.BasicDataSource">		
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="${db.master.dbUrl}" />
		<property name="username" value="${db.master.userName}" />
		<property name="password" value="${db.master.passWord}" />
		<!-- 连接池启动时创建的初始化连接数量(默认值为0) -->
		<property name="initialSize" value="1"/> 
		<!-- 连接池中可同时连接的最大的连接数 默认 8-->
		<property name="maxActive" value="100"/>
		<!-- 连接池中最小的空闲的连接数,低于这个数量会被创建新的连接 -->
		<property name="minIdle" value="5"/>  
		<!-- 连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果设置为负数表示不限制 默认8 -->
		<property name="maxIdle" value="30"/>
		<!-- 最大等待时间,当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待 -->
		<property name="maxWait" value="60000"/>
		<!-- 超过removeAbandonedTimeout时间后,是否进 行没用连接(废弃)的回收(默认为false,调整为true)-->
		<property name="removeAbandoned" value="true"/>
		<!-- 超过时间限制,回收没有用(废弃)的连接(默认为 300秒,调整为180) -->
		<property name="removeAbandonedTimeout" value="180"/>  
		<!-- 默认提交 -->
		<property name="defaultAutoCommit" value="true"/>
		<!-- 打开检查,用异步线程evict进行检查 -->
		<property name="testWhileIdle" value="true"/> 
		<!-- 就是在进行borrowObject进行处理时,对拿到的connection进行validateObject校验 -->
        <property name="testOnBorrow" value="false"/>
        <!-- 就是在进行returnObject对返回的connection进行validateObject校验,个人觉得对数据库连接池的管理意义不大 -->
        <property name="testOnReturn" value="false"/>
        <!--  代表检查的sql -->
        <property name="validationQuery" value="select 1 from dual"/>
        <!-- 代表在执行检查时,通过statement设置,statement.setQueryTimeout(validationQueryTimeout)  -->
        <property name="validationQueryTimeout" value="1"/>
        <property name="timeBetweenEvictionRunsMillis" value="30000"/>
        <!-- 代表每次检查链接的数量,建议设置和maxActive一样大,这样每次可以有效检查所有的链接.  -->
        <property name="numTestsPerEvictionRun" value="100"/>
	</bean>
	
	
		
	<!-- portal master数据库 -->
	<bean id="dataSource_website" class="org.apache.commons.dbcp.BasicDataSource">		
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="${db.website.dbUrl}" />
		<property name="username" value="${db.website.userName}" />
		<property name="password" value="${db.website.passWord}" />
		<!-- 连接池启动时创建的初始化连接数量(默认值为0) -->
		<property name="initialSize" value="1"/> 
		<!-- 连接池中可同时连接的最大的连接数 默认 8-->
		<property name="maxActive" value="100"/>
		<!-- 连接池中最小的空闲的连接数,低于这个数量会被创建新的连接 -->
		<property name="minIdle" value="5"/>  
		<!-- 连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果设置为负数表示不限制 默认8 -->
		<property name="maxIdle" value="30"/>
		<!-- 最大等待时间,当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待 -->
		<property name="maxWait" value="60000"/>
		<!-- 超过removeAbandonedTimeout时间后,是否进 行没用连接(废弃)的回收(默认为false,调整为true)-->
		<property name="removeAbandoned" value="true"/>
		<!-- 超过时间限制,回收没有用(废弃)的连接(默认为 300秒,调整为180) -->
		<property name="removeAbandonedTimeout" value="180"/>  
		<!-- 默认提交 -->
		<property name="defaultAutoCommit" value="true"/>
		<!-- 打开检查,用异步线程evict进行检查 -->
		<property name="testWhileIdle" value="true"/> 
		<!-- 就是在进行borrowObject进行处理时,对拿到的connection进行validateObject校验 -->
        <property name="testOnBorrow" value="false"/>
        <!-- 就是在进行returnObject对返回的connection进行validateObject校验,个人觉得对数据库连接池的管理意义不大 -->
        <property name="testOnReturn" value="false"/>
        <!--  代表检查的sql -->
        <property name="validationQuery" value="select 1 from dual"/>
        <!-- 代表在执行检查时,通过statement设置,statement.setQueryTimeout(validationQueryTimeout)  -->
        <property name="validationQueryTimeout" value="1"/>
        <property name="timeBetweenEvictionRunsMillis" value="30000"/>
        <!-- 代表每次检查链接的数量,建议设置和maxActive一样大,这样每次可以有效检查所有的链接.  -->
        <property name="numTestsPerEvictionRun" value="100"/>
	</bean>
	


4  然后是配置转换器

	<bean id="data_dataSource" class="com.shangde.common.datesource.DynamicDataSource">
	 <property name="targetDataSources">
		<map key-type="java.lang.String">
			<entry key="dataSource_website" value-ref="dataSource_website" />
			<entry key="dataSource_master" value-ref="dataSource_master" />
		</map>
	 </property>
	 <property name="defaultTargetDataSource" ref="dataSource_master" />
   </bean>

5配置切面

<!-- 动态数据源切换aop 先于事务的aop  -->
<bean id="dataSourceInterceptor" class="com.shangde.common.datesource.DataSourceInterceptor" />
   <aop:config>
	<aop:aspect id="dataSourceAspect" ref="dataSourceInterceptor">
		<aop:pointcut id="dsMaster" expression="execution(* com.shangde.master.mobile.*.service..*.*(..))" />
		<aop:pointcut id="dsWebsite" expression="execution(* com.shangde.master.website.*.service..*.*(..))" />
		<aop:before method="setdataSourceMaster" pointcut-ref="dsMaster"/>
		<aop:before method="setdataSourceWebsite" pointcut-ref="dsWebsite"/>
	</aop:aspect>
  </aop:config>
	
6.配置sessionfactory

<bean id="sqlSessionFactory_master" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="data_dataSource" />
		<!-- 全局sqlMapConfig.xml公共配置文件路径 -->
		<property name="configLocation" value="classpath:/mybatis_master.xml" />
  		<property name="typeAliasesPackage" value="com.shangde.master.dmo.*" />
  	</bean>

7   AbstractRoutingDataSource转换器代码 

DynamicDataSource

package com.shangde.common.datesource;

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

public class DynamicDataSource extends AbstractRoutingDataSource {
	@Override
	protected Object determineCurrentLookupKey() {
		return CustomerContextHolder.getCustomerType();
	}
}

DataSourceInterceptor

package com.shangde.common.datesource;

import org.aspectj.lang.JoinPoint;
public class DataSourceInterceptor {

	public void setdataSourceMaster(JoinPoint jp) {
		CustomerContextHolder.setCustomerType("dataSource_master");
	}
	
	public void setdataSourceWebsite(JoinPoint jp) {
		CustomerContextHolder.setCustomerType("dataSource_website");
	}
}

CustomerContextHolder
package com.shangde.common.datesource;

public class CustomerContextHolder {
	public static final String DATA_SOURCE_MASTER = "dataSource_master";
	public static final String DATA_SOURCE_WEBSITE = "dataSource_website";
	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();
	}
}


8 好了代码就这么多了  一些失误的配置自行解决。

原理: AbstractRoutingDataSource 能够通过key的值在 map中寻找对应的 值,也就是对应的数据源。然而怎么才能获得key的值呢?。我们对不同数据源分包(package)管理,同一包下的代码使用了同一数据源。通过AOP来实现,大家都知道web开发常见是三层结构:controller、service、dao。假设我们创建了2个包  package1 ,package2分别对应不同的数据源db1 和db2。在对应的包下面创建 我们 service 包 和 dao 包 ,我们只需要用AOP切 不同数据源下的包名的service的目录即可,当controller层调用service 类时,发现它在 package1 .service 包下,这是spring 切面就会调用 切面类,设置代表数据源的字符串db1然后AbstractRoutingDataSource根据db1這个key在map中找到对应的数据源 db1 .然后我们只是找到了這个数据源,那么怎么用呢。

我们发现AbstractRoutingDataSource   extends   AbstractDataSource 发现他继承自 AbstractDataSource。而且刚才我们已经找到了我们到底要那个数据源,这样我们就可以直接使用 這个实现了 AbstractRoutingDataSource   的类作为数据源了。






评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值