spring 动态数据源

1.Spring 配置文件datasource
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource" destroy-method="close">
	        <!-- 指定连接数据库的URL-->  
		<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/ConfigurationRepository?&autoReconnect=true&characterEncoding=utf8" />
		<!-- 指定连接数据库的驱动-->  
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>  
        <!-- 指定连接数据库的用户名-->  
        <property name="user" value="root"/>  
        <!-- 指定连接数据库的密码-->  
        <property name="password" value="root"/>  
        <!-- 指定连接池中保留的最大连接数. Default:15-->  
        <property name="maxPoolSize" value="20"/>  
        <!-- 指定连接池中保留的最小连接数-->  
        <property name="minPoolSize" value="3"/>  
         <!--关闭连接时,是否提交未提交的事务,默认为false,即关闭连接,回滚未提交的事务 -->   
        <property name="autoCommitOnClose" value="false" />
        <!-- 当连接池连接耗尽时,客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒。默认: 0 -->   
        <property name="checkoutTimeout" value="3000"/>  
        <!-- 指定连接池的初始化连接数  取值应在minPoolSize 与 maxPoolSize 之间.Default:3-->  
        <property name="initialPoolSize" value="3"/>  
        <!-- 最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。 Default:0-->  
        <property name="maxIdleTime" value="60"/>  
        <!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数. Default:3-->  
        <property name="acquireIncrement" value="2"/>  
        <!-- JDBC的标准,用以控制数据源内加载的PreparedStatements数量。  
        但由于预缓存的statements属于单个connection而不是整个连接池所以设置这个参数需要考虑到多方面的因数.如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default:0-->  
        <property name="maxStatements" value="100"/>  
        <!-- 每60秒检查所有连接池中的空闲连接.Default:0 -->  
        <property name="idleConnectionTestPeriod" value="1"/>
	</bean>
	

	<!-- 动态数据源配置 -->
	<bean id="dynamicDataSource" class="org.mybatis.Dynamic.DynamicDataSource">
		<property name="targetDataSources">
			<map key-type="java.lang.String">
				<!-- 动态初始化map -->
			</map>
		</property>
		 <property name="defaultTargetDataSource" ref="dataSource" />    
	</bean>

	<bean id="applicationEventListener" class="org.mybatis.Dynamic.DynamicCreateDataSourceBean">
		<property name="dynamicDataSource" ref="dynamicDataSource"></property>
		<property name="dataSource" ref="dataSource"></property>
	</bean>
<span style="white-space:pre">	</span><!--动态数据源配置-->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dynamicDataSource" ></property>  
	</bean>

	<!-- 事务管理器配置, 使用jdbc事务 -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dynamicDataSource" />
	</bean>

	<tx:annotation-driven transaction-manager="transactionManager" />
	
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="get*" read-only="true" />
			<tx:method name="find*" read-only="true" />
			<tx:method name="query*" read-only="true" />
			<tx:method name="is*" read-only="true" />
			<tx:method name="do*" propagation="REQUIRES_NEW"
				rollback-for="java.lang.Exception" />
			<tx:method name="save*" propagation="REQUIRED"
				rollback-for="java.lang.Exception" />
			<tx:method name="update*" propagation="REQUIRED"
				rollback-for="java.lang.Exception" />
			<tx:method name="delete*" propagation="REQUIRED"
				rollback-for="java.lang.Exception" />
		</tx:attributes>
	</tx:advice>
	
	<!-- 事务拦截的类及方法  -->
	<aop:config proxy-target-class="true">
	<!--  其中第一个*代表返回值,第二*代表service下子包,第三个*代表方法名,“(..)”代表方法参数。 -->
		<aop:advisor pointcut="execution(* xt.city.edi.*.*(..))"
			advice-ref="txAdvice" />
	</aop:config>


【动态设置数据源的上下文类】

package org.mybatis.Dynamic;

public class DataSourceContextHolder {  
 
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();  
  
    public static void setDbType(String dbType) {  
        contextHolder.set(dbType);  
    }  
  
    public static String getDbType() {  
        return ((String) contextHolder.get());  
    }  
  
    public static void clearDbType() {  
        contextHolder.remove();  
    }  
}  

【动态创建数据源类】

package org.mybatis.Dynamic;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ChildBeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;

import com.mchange.v2.c3p0.ComboPooledDataSource;


/**
 *  实现一个实现ApplicationContextAware和ApplicationListener接口的类DynamicDataSourceC3p0,
 * 实现ApplicationContextAware是为了得到ApplicationContext,
 * 实现了ApplicationListener是为了配置spring的加载事件。
 *
 */
public class DynamicCreateDataSourceBean  implements ApplicationContextAware,ApplicationListener{

	private ConfigurableApplicationContext app;

	private DynamicDataSource dynamicDataSource;
	
	public DynamicDataSource getDynamicDataSource() {
		return dynamicDataSource;
	}
	public void setDynamicDataSource(DynamicDataSource dynamicDataSource) {
		this.dynamicDataSource = dynamicDataSource;
	}

	private DataSource dataSource;

	public DataSource getDataSource() {
		return dataSource;
	}

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	

	public void setApplicationContext(ApplicationContext app) throws BeansException {
		this.app = (ConfigurableApplicationContext)app;
	}

	
	public void onApplicationEvent(ApplicationEvent event) {
		// 如果是容器刷新事件OR Start Event
		if (event instanceof ContextRefreshedEvent) {
			try {
				regDynamicBean();
			} catch (IOException e) {
				e.printStackTrace();
			}
			//System.out.println(event.getClass().getSimpleName()+" 事件已发生!");
		}
		
	}

	private void regDynamicBean() throws IOException {
		// 解析属性文件,得到数据源Map
		Map<String, DataSourceInfo> mapCustom = parsePropertiesFile();
		addSourceBeanToApp2(mapCustom);
		// 把数据源bean注册到容器中
		/*if(getDataSource() instanceof ComboPooledDataSource){
			addSourceBeanToApp2(mapCustom);
		}else{*/
		  // addSourceBeanToApp(mapCustom);
		/*}*/
	}
	
	
	
	/** 
     * 功能说明:根据DataSource创建bean并注册到容器中 
     * @param acf 
     * @param mapCustom 
     */  
    private void addSourceBeanToApp2(Map<String, DataSourceInfo> mapCustom) {  
        DefaultListableBeanFactory acf = (DefaultListableBeanFactory) app.getAutowireCapableBeanFactory();  
        BeanDefinition beanDefinition;  
        Iterator<String> iter = mapCustom.keySet().iterator();  
        Map<Object,Object> targetDataSources = new LinkedHashMap<Object, Object>();  
        while(iter.hasNext()){  
            String beanKey = iter.next();  
            // 得到Bean定义,并添加到容器中  
            beanDefinition = new ChildBeanDefinition("dataSource");  
            // 注意:必须先注册到容器中,再得到Bean进行修改,否则数据源属性不能有效修改  
            acf.registerBeanDefinition(beanKey, beanDefinition);  
            // 再得到数据源Bean定义,并修改连接相关的属性  
            ComboPooledDataSource cpds = (ComboPooledDataSource)app.getBean(beanKey);;  
  
            cpds.setJdbcUrl(mapCustom.get(beanKey).connUrl);  
            cpds.setUser(mapCustom.get(beanKey).userName);  
            cpds.setPassword(mapCustom.get(beanKey).password);  
            targetDataSources.put(beanKey, app.getBean(beanKey));  
          }  
          //将创建的map对象set到 targetDataSources;  
         dynamicDataSource.setTargetDataSources(targetDataSources);  
          
         //必须执行此操作,才会重新初始化AbstractRoutingDataSource 中的 resolvedDataSources,也只有这样,动态切换才会起效  
        dynamicDataSource.afterPropertiesSet();  
    }  
	
	
	/**
	 * 功能说明:根据DataSource创建bean并注册到容器中
	 * @param mapCustom
	 */
	private void addSourceBeanToApp(Map<String, DataSourceInfo> mapCustom) {
		DefaultListableBeanFactory acf = (DefaultListableBeanFactory) app  
                .getAutowireCapableBeanFactory();  
  
        String DATASOURCE_BEAN_CLASS = "org.apache.commons.dbcp.BasicDataSource";  
        String DATASOURCE_BEAN_CLASS2 = "com.mchange.v2.c3p0.ComboPooledDataSource";
        BeanDefinitionBuilder bdb;  
          
        Iterator<String> iter = mapCustom.keySet().iterator();  
          
        Map<Object,Object> targetDataSources = new LinkedHashMap<Object, Object>();  
          
        //ChildBeanDefinition baseBeanComm = new ChildBeanDefinition("dataSource");  
        //  将默认数据源放入 targetDataSources map中  
        //targetDataSources.put("dataSource", app.getBean("dataSource"));  
  
        // 根据数据源得到数据,动态创建数据源bean 并将bean注册到applicationContext中去  
        while (iter.hasNext()) {  
            //  bean ID  
            String beanKey = iter.next();  
            //  创建bean 
            bdb =	BeanDefinitionBuilder.childBeanDefinition("dataSource");
            bdb = BeanDefinitionBuilder.rootBeanDefinition(DATASOURCE_BEAN_CLASS2);  
            bdb.getBeanDefinition().setAttribute("id", beanKey);  
            //bdb.addPropertyValue("driverClass", "com.mysql.jdbc.Driver");  
            bdb.addPropertyValue("jdbcUrl", mapCustom.get(beanKey).connUrl);  
            bdb.addPropertyValue("user", mapCustom.get(beanKey).userName);  
            bdb.addPropertyValue("password", mapCustom.get(beanKey).password);  
                
            //  注册bean  
            acf.registerBeanDefinition(beanKey, bdb.getBeanDefinition());  
              
            //  放入map中,注意一定是刚才创建bean对象  
            targetDataSources.put(beanKey, app.getBean(beanKey));  
              
        }  
        //  将创建的map对象set到 targetDataSources;  
        dynamicDataSource.setTargetDataSources(targetDataSources);  
          
        //  必须执行此操作,才会重新初始化AbstractRoutingDataSource 中的 resolvedDataSources,也只有这样,动态切换才会起效  
        dynamicDataSource.afterPropertiesSet();  
	}

	/**
	 * 功能说明:GET city.properties
	 * 
	 * @return
	 * @throws IOException
	 */
	private Map<String, DataSourceInfo> parsePropertiesFile()
			throws IOException {
		InputStream ips2 = this.getClass().getClassLoader() .getResourceAsStream("city.properties"); 
		InputStreamReader isr=new InputStreamReader(ips2, "UTF-8");
		BufferedReader br = new BufferedReader(isr);
		Map<String, DataSourceInfo> mds = new HashMap<String, DataSourceInfo>();
		String line = "";
		while(( line = br.readLine())!=null){
			String[] keyVal = line.split(".url=");
			DataSourceInfo dsi  = new DataSourceInfo();  
			String[] connStr = StringUtils.split(keyVal[1],"|");
			System.out.println(connStr[0]);
			System.out.println(connStr[1]);
			System.out.println(connStr[2]);
			
			dsi.connUrl = connStr[0];
			dsi.userName = connStr[1].split("=")[1];
			dsi.password = connStr[2].split("=")[1];
			mds.put(keyVal[0], dsi);
		}
		br.close();
		isr.close();
		ips2.close();
		return mds;
	}
	
	//	自定义数据结构
	private class DataSourceInfo{  

		public String connUrl;  
		public String userName;  
		public String password;  
		  
		public String toString() {
			return "(url:" + connUrl + ", username:" + userName + ", password:"
				+ password + ")";
		} 
	}  
	
	

}


Spring 中自动调用的类

package org.mybatis.Dynamic;


import java.util.logging.Logger;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;


public class DynamicDataSource extends AbstractRoutingDataSource {  
	
	 static Logger log = Logger.getLogger("DynamicDataSource");  
	
    @Override  
    protected Object determineCurrentLookupKey() {  
        return DataSourceContextHolder.getDbType();  
    }  
    
}


调用逻辑请看[AbstractRoutingDataSource.java]


使用方法: DataSourceContextHolder.setDbType("datasourceName");"datasourceName"为动态创建的数据源bean名称





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值