spring mybatis 多数据源整合 动态数据源例子

102 篇文章 2 订阅
动态数据源
多数据源问题很常见,例如读写分离数据库配置。

我们很多项目中业务都需要涉及到多个数据源,最简单的做法就是直接在Java代码里面lookup需要的数据源,但是这样的做法很明显耦合度太高了,

而且当逻辑流程不够严谨的时候就会出现各种大家不愿意看到的问题,由于我们现在的大多项目已经离不开spring了,spring也提供各种强大的功能,

很明显这种动态数据源功能也包括在内,具体实现类请看

org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource

1)首先配置多个datasource和AOP

datasource.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-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" default-autowire="byName">
		
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="DS" />
		<property name="typeAliasesPackage" value="com.letv.ofc.domain" />
		<property name="configLocation" value="classpath:mybatis-config.xml" />
		<!-- mapper and resultmap path -->
		<property name="mapperLocations">
			<list>
				<value>classpath:sqlmap/mysql/*/*.xml</value>
			</list>
		</property>
	</bean>
	
	<bean id="DS" class="DynamicDataSource">  
	    <property name="targetDataSources">
	        <map key-type="java.lang.String">
	            <entry value-ref="ofc" key="dataSourceOfc"/>
	            <entry value-ref="order" key="dataSourceOrder"/>
	        </map>
	    </property>
	    <property name="defaultTargetDataSource" ref="ofc"/>
	</bean>
	
	<bean id="ofc" class="org.apache.commons.dbcp.BasicDataSource" >
		<property name="driverClassName" value="${mysql.db.driverClassName}" />
		<property name="defaultAutoCommit" value="true" />
	    <property name="url" value="${ofc.db.url}"/>
	    <property name="username" value="${ofc.db.username}" />
		<property name="password" value="${ofc.db.password}" />
		<property name="initialSize" value="${ofc.db.initialSize}" />
		<property name="maxActive" value="${ofc.db.maxActive}" />
	    <property name="maxIdle" value="${ofc.db.maxIdle}" />
		<property name="maxWait" value="${ofc.db.maxWait}" />
		<property name="minIdle" value="${ofc.db.minIdle}" />
		<property name="timeBetweenEvictionRunsMillis" value="${ofc.db.timeBetweenEvictionRunsMillis}" />
		<property name="numTestsPerEvictionRun" value="${ofc.db.numTestsPerEvictionRun}" />
		<property name="minEvictableIdleTimeMillis" value="${ofc.db.minEvictableIdleTimeMillis}" />
	</bean>
	
	<bean id="order" class="org.apache.commons.dbcp.BasicDataSource" >
		<property name="driverClassName" value="${mysql.db.driverClassName}" />
		<property name="defaultAutoCommit" value="true" />
	    <property name="url" value="${order.db.url}"/>
	    <property name="username" value="${order.db.username}" />
		<property name="password" value="${order.db.password}" />
		<property name="initialSize" value="${order.db.initialSize}" />
		<property name="maxActive" value="${order.db.maxActive}" />
	    <property name="maxIdle" value="${order.db.maxIdle}" />
		<property name="maxWait" value="${order.db.maxWait}" />
		<property name="minIdle" value="${order.db.minIdle}" />
		<property name="timeBetweenEvictionRunsMillis" value="${order.db.timeBetweenEvictionRunsMillis}" />
		<property name="numTestsPerEvictionRun" value="${order.db.numTestsPerEvictionRun}" />
		<property name="minEvictableIdleTimeMillis" value="${order.db.minEvictableIdleTimeMillis}" />
	</bean>
	
	<aop:config>  
	    <aop:aspect id="dataSourceAspect" ref="dataSourceInterceptor">
	        <aop:pointcut id="ofcAspect" expression="execution(* ofc.manager..*.*(..))" />>
	        <aop:pointcut id="orderAspect" expression="execution(* order.manager..*.*(..))" />
             <aop:before pointcut-ref="ofcAspect" method="setDataSourceOfc" />
	        <aop:before pointcut-ref="orderAspect" method="setDataSourceOrder" />
	    </aop:aspect>
	</aop:config>
	


2)类的实现

 本例子是通过Spring AOP来实现动态datasource的设置的。具体的类如下:


2.1  DataSourceInterceptor类的实现。它出 Spring AOP调用方法的实现。
 该类的方法就是在AOP拦截时候,会被spring AOP调用,动态地根据被执行的方法来设置对应的DS

2.2 DynamicDataSource extends AbstractRoutingDataSource,这个是实现spring 动态数据源必须要做的。
spring 规定用户必须继承  AbstractRoutingDataSource并实现:AbstractRoutingDataSource的determineCurrentLookupKey。
另外考虑到多线程环境,所以一般要使用ThreadLocal类来包装线程特有的变量,如下文的DatabaseContextHolder:contextHolder变量。

sqlSessionFactory的dataSource属性设置为DynamicDataSource 后,当要访问mysql时候,spring会调用 DynamicDataSource.determineCurrentLookupKey()来获得key,然后根据下面的xml配置来得到targetDataSources,根据key,比如"dataSourceOfc",就得到了对应的value,从而得到了 数据库访问的名字和属性,比如" ofc"

<bean id="dataSource" class="com.letv.ofc.service.base.DynamicDataSource">  
   <property name="targetDataSources">
       <map key-type="java.lang.String">
           <entry value-ref="ofc" key="dataSourceOfc"/>
           <entry value-ref="order" key="dataSourceOrder"/>
       </map>
   </property>
   <property name="defaultTargetDataSource" ref="ofc"/>
</bean>
 
  这里关键是在调用不同的类的方法时候,要利用AOP来设置不同的key,比如 "dataSourceOfc",或者"dataSourceOrder",而这个则是通过调用对象DataSourceInterceptor的不同方法来完成的。

该类通过 spring AOP (如下的xml的配置方式)来实现的。
<aop:config>  
   <aop:aspect id="dataSourceAspect" ref="dataSourceInterceptor">
       <aop:pointcut id="ofcAspect" expression="execution(* com.letv.ofc.manager..*.*(..))" />
       <aop:pointcut id="orderAspect" expression="execution(* com.letv.order.manager..*.*(..))" />
       <aop:before pointcut-ref="ofcAspect" method="setDataSourceOfc" />      
       <aop:before pointcut-ref="orderAspect" method="setDataSourceOrder" />     
   </aop:aspect>
</aop:config>

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();
	}
}

public class DataSourceInterceptor {
	public void setDataSourceOfc(JoinPoint jp) {
		DatabaseContextHolder.setCustomerType("dataSourceOfc");
	}
	public void setDataSourceOrder(JoinPoint jp) {
		DatabaseContextHolder.setCustomerType("dataSourceOrder");
	}
}



DynamicDataSource extends AbstractRoutingDataSource {
//@Override
	protected Object determineCurrentLookupKey() {
		return DatabaseContextHolder.getCustomerType();
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值