spring的AbstractRoutingDataSource 多数据源切换

之前参加了一个项目,此项目涉及到多个数据源的定位与切换。此项目使用Spring的AbstractRoutingDataSource抽象类来进行完成的,下面看一下源码:

package org.springframework.jdbc.datasource.lookup;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.datasource.AbstractDataSource;
import org.springframework.util.Assert;

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {

	private Map<Object, Object> targetDataSources;//多数据源目标key-value

	private Object defaultTargetDataSource;//默认当前数据源

       /**
       * 宽容回退.
       *在通过key来切换数据源如果没有找到,或者key本身就是null这个时候是否要使	用默认数据源。		 	
       * true:使用默认数据源;
       * false:抛出异常Cannot determine target DataSource * for lookup key lookupKey
       * 默认值:true ,表示使用宽容回退
       */
	private boolean lenientFallback = true;

       /**
       * JNDI通过key来查找数据源。
       * DataSourceLookup 包含一个方法: 
       * DataSource getDataSource(String dataSourceName) throws    
       *  DataSourceLookupFailureException; 
       */
	private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();

       /**
        * 所有备选的数据库。
        * key-value(简称-DataSource对象)
        */
	private Map<Object, DataSource> resolvedDataSources;

	private DataSource resolvedDefaultDataSource; //默认的数据源


	public void setTargetDataSources(Map<Object, Object> targetDataSources) {
		this.targetDataSources = targetDataSources;
	}

	public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
		this.defaultTargetDataSource = defaultTargetDataSource;
	}

	public void setLenientFallback(boolean lenientFallback) {
		this.lenientFallback = lenientFallback;
	}

	public void setDataSourceLookup(DataSourceLookup dataSourceLookup) {
		this.dataSourceLookup = (dataSourceLookup != null ? dataSourceLookup : new JndiDataSourceLookup());
	}


	@Override
	public void afterPropertiesSet() {
		if (this.targetDataSources == null) {//配置文件中一定要配置
			throw new IllegalArgumentException("Property 'targetDataSources' is required");
		}
		this.resolvedDataSources = new HashMap<Object, DataSource>(this.targetDataSources.size());
		for (Map.Entry<Object, Object> entry : this.targetDataSources.entrySet()) {
			Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());
			DataSource dataSource = resolveSpecifiedDataSource(entry.getValue());
			this.resolvedDataSources.put(lookupKey, dataSource);
		}
		if (this.defaultTargetDataSource != null) {
			this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
		}
	}

	protected Object resolveSpecifiedLookupKey(Object lookupKey) {
		return lookupKey;
	}

        //通过数据源真实名称,查询DataSource对象
	protected DataSource resolveSpecifiedDataSource(Object dataSource) throws IllegalArgumentException {
		if (dataSource instanceof DataSource) {
			return (DataSource) dataSource;
		}
		else if (dataSource instanceof String) {
			return this.dataSourceLookup.getDataSource((String) dataSource);
		}
		else {
			throw new IllegalArgumentException(
					"Illegal data source value - only [javax.sql.DataSource] and String supported: " + dataSource);
		}
	}


	@Override
	public Connection getConnection() throws SQLException {
		return determineTargetDataSource().getConnection();
	}

	@Override
	public Connection getConnection(String username, String password) throws SQLException {
		return determineTargetDataSource().getConnection(username, password);
	}

	@Override
	@SuppressWarnings("unchecked")
	public <T> T unwrap(Class<T> iface) throws SQLException {
		if (iface.isInstance(this)) {
			return (T) this;
		}
		return determineTargetDataSource().unwrap(iface);
	}

	@Override
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		return (iface.isInstance(this) || determineTargetDataSource().isWrapperFor(iface));
	}

        //通过简称查询DataSource对象
	protected DataSource determineTargetDataSource() {
		Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
		Object lookupKey = determineCurrentLookupKey();
		DataSource dataSource = this.resolvedDataSources.get(lookupKey);
		if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
			dataSource = this.resolvedDefaultDataSource;
		}
		if (dataSource == null) {
			throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
		}
		return dataSource;
	}

        //子类重写determineCurrentLookupKey()方法,返回要使用的数据源的key
	protected abstract Object determineCurrentLookupKey();

}
类DynamicRoutingDataSource 继承抽象类 AbstractRoutingDataSource
package com.cpic.auap.core.datasource;

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

/**
 * 动态路由DataSource,根据在ThreadLocal中设置的DataSourceType动态取得不同的数据源。
 * 
 */
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {

	@Override
	protected Object determineCurrentLookupKey() {
		return DataSourceContextHolder.determineCurrentLookupKey();
	}
}

/**
 * 在ThreadLocal中保存当前线程需要使用的String。
 * 
 * @see {@link DynamicRoutingDataSource}
 */
public class DataSourceContextHolder {
	private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

	public static void setDataSource(String type) {
		// 不使用mycat时这里切换数据源的地方进行屏蔽
		contextHolder.set(type);
	}

	public static String determineCurrentLookupKey() {
		String dbtype =  (String)contextHolder.get();
		return dbtype;
	}
	
	public static String getDataSource() {
		String dbtype =  (String)contextHolder.get();
		if(dbtype == null ){//获取默认数据源
			dbtype = getDefaultDataSource();
		}
		return dbtype;
	}
	
	public static String getDefaultDataSource() {
		//获取默认数据源
		String defaultDbtype = (String) SpringContextHolder.getBean("defaultDbtype");
		defaultDbtype = defaultDbtype.trim();
		
		 SpringContextHolder.getBean("dbtypeMap");
		return defaultDbtype;
	}

	public static void clearString() {
		contextHolder.remove();
	}
}
context-datasource.spring.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:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
       http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.0.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
	 
    <bean id="dataSourceB01"
          class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="defaultAutoCommit" value="false" />
    </bean>
    <bean id="dataSourceB02"
          class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="defaultAutoCommit" value="false" />
    </bean>
    <bean id="dataSourceB03"
          class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="defaultAutoCommit" value="false" />
    </bean>
    <bean id="dataSourceC01"
          class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="defaultAutoCommit" value="false" />
    </bean>
	
   <bean id="defaultDbtype" class="java.lang.String">
		<constructor-arg><!-- 默认数据源的dbtype -->
			<value>102</value>
		</constructor-arg>
	</bean>
	
	<!-- 数据库分库编码映射Map -->
	<bean id="dbtypeMap" class="java.util.HashMap">
		<constructor-arg>
			<map key-type="java.lang.String">
				<!-- 业务数据库 -->
				<entry key="101" value-ref="dataSourceB01" />
				<entry key="102" value-ref="dataSourceB02" />
				<entry key="103" value-ref="dataSourceB03" />
				<entry key="104" value-ref="dataSourceB04" />
	
				<!-- 配置数据库 -->
				<entry key="201" value-ref="dataSourceC01" />
				
				 <!-- 备份数据库 -->
				<entry key="301" value-ref="dataSourceD01" />
			</map>
		</constructor-arg>
	</bean>
	
     <bean id="dataSource" class="com.cpic.auap.core.datasource.DynamicRoutingDataSource">
		<property name="targetDataSources" ref="dbtypeMap"/>
		<!-- 默认数据源为配置库 -->
		<property name="defaultTargetDataSource" ref="dataSourceB03"/>
	</bean> 
	
</beans>









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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值