`
jinyanhui2008
  • 浏览: 312145 次
  • 性别: Icon_minigender_1
  • 来自: 青岛
社区版块
存档分类
最新评论

spring 多数据库支持,动态切换数据库

阅读更多

为了备份特将此新的发到博客当中以备查找。

数据库的动态切换在很多项目当中都有应用,经我查阅了多篇文档,整合思路最终成功实现数据源的动态切换功能,并稳定运行了一段时间未发现异常。

 

我的数据源切换时根据域名并配合spring来切换的,不同的域名访问不同的数据源,当然可以根据其他的需求进行动态切换。

 

首先需要配置一个过滤器来过滤域名,并动态切换数据源。

web.xml 增加如下代码:

     <!-- 根据域名动态切换数据源 -->
     <filter>
    	<filter-name>ServerFilter</filter-name>
    	<filter-class>com.wfy.filter.ServerFilter</filter-class>
    </filter>
    <filter-mapping>
    	<filter-name>ServerFilter</filter-name>
    	<url-pattern>/*</url-pattern>
    </filter-mapping>

 

对应写入相应的java类:

package com.wfy.filter;

import java.io.File;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import com.wfy.multiData.SpObserver;

public class ServerFilter extends HttpServlet implements Filter {

	private static final long serialVersionUID = 6452049924844786456L;
	private static FilterConfig filterConfig;
	public void destroy() {
		// TODO Auto-generated method stub

	}
	public void init(FilterConfig filterConfig) throws ServletException
	{
	}

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain filterChain) throws IOException, ServletException {
		/**
		 * 需要在此处操作数据源,将数据源切换到新定义好的数据源当中。
		 */
		
		System.out.println("request.getServerName():_"+request.getServerName());
		String url = Thread.currentThread().getContextClassLoader().getResource("") + "dataSource/_"+request.getServerName()+".xml";
		File f=new File(url.substring(6, url.length()));
		//确认数据源配置文件存在,切换数据源
		if(f.exists()){
			SpObserver.putSp("_"+request.getServerName());
		}else{
		//如果数据源配置文件不存在,使用默认数据源
			SpObserver.putSp("_dataSource");
		}
		
		filterChain.doFilter(request, response);
	}
}

 

SpObserver类:

package com.wfy.multiData;

/**
 * @author 金鑫
 *
 */
public class SpObserver {
	private static ThreadLocal local = new ThreadLocal();

	public static void putSp(String sp) {
		local.set(sp);
	}

	public static String getSp() {
		return (String)local.get();
	}
}

  这样过滤器的设置就算完成了,下面是spring的设置

spring配置文件:

	<!-- 数据库模块 初始化 -->
	<bean id="DynamicLoadBean" class="com.wfy.multiData.DynamicLoadBean" />
	<!--可以通过他动态增加新的数据源-->
	<bean id="InitDataSource" class="com.wfy.init.InitDataSource"></bean>

	<!--配置统一数据源,将来系统中使用的数据源将从他当中获取-->
	<bean id="DataSource" class="com.wfy.multiData.MultiDataSource">
		<property name="dataSource">  
			<ref bean="_dataSource" />  
		</property>  
	</bean>

 

MultiDataSource类:

package com.wfy.multiData;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.wfy.exceptionAdvisor.BusinessException;

/**
 * @author 金鑫
 *
 */
public class MultiDataSource implements DataSource,ApplicationContextAware {

	private static final Log log = LogFactory.getLog(MultiDataSource.class);
	private ApplicationContext applicationContext = null;
	private DataSource dataSource = null;
	/* (non-Javadoc)
	 * @see javax.sql.DataSource#getConnection()
	 */
	public Connection getConnection() throws SQLException {
		return getDataSource().getConnection();
	}

	/* (non-Javadoc)
	 * @see javax.sql.DataSource#getConnection(java.lang.String, java.lang.String)
	 */
	public Connection getConnection(String arg0, String arg1)
			throws SQLException {
		return getDataSource().getConnection(arg0, arg1);
	}

	/* (non-Javadoc)
	 * @see javax.sql.DataSource#getLogWriter()
	 */
	public PrintWriter getLogWriter() throws SQLException {
		return getDataSource().getLogWriter();
	}

	/* (non-Javadoc)
	 * @see javax.sql.DataSource#getLoginTimeout()
	 */
	public int getLoginTimeout() throws SQLException {
		return getDataSource().getLoginTimeout();
	}

	/* (non-Javadoc)
	 * @see javax.sql.DataSource#setLogWriter(java.io.PrintWriter)
	 */
	public void setLogWriter(PrintWriter arg0) throws SQLException {
		getDataSource().setLogWriter(arg0);
	}

	/* (non-Javadoc)
	 * @see javax.sql.DataSource#setLoginTimeout(int)
	 */
	public void setLoginTimeout(int arg0) throws SQLException {
		getDataSource().setLoginTimeout(arg0);
	}

	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}
	
	public DataSource getDataSource(String dataSourceName){
		log.debug("dataSourceName:"+dataSourceName);
		try{
			if(dataSourceName==null||dataSourceName.equals("")){
				return this.dataSource;
			}
			return (DataSource)this.applicationContext.getBean(dataSourceName);
		}catch(NoSuchBeanDefinitionException ex){
			throw new BusinessException("没有 <name:"+dataSourceName+"> 数据源 在系统当中!");
		}
	}
	
	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	public DataSource getDataSource(){
		String sp = SpObserver.getSp();
		return getDataSource(sp);
	}

	@Override
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public <T> T unwrap(Class<T> iface) throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}
}

 

DynamicLoadBean类:

package com.wfy.multiData;

import java.io.IOException;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.ResourceEntityResolver;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.AbstractXmlApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;

/**
 * 使用方法loadBean()向spring的beanFactory动态地装载bean,该方法的参数configLocationString等同于
 * spring配置中的contextConfigLocation,同样支持诸如"/WEB-INF/ApplicationContext-*.xml"的写法。
 * @author FanGang
 *
 */
public class DynamicLoadBean implements ApplicationContextAware{

	private XmlWebApplicationContext applicationContext = null;
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = (XmlWebApplicationContext)applicationContext;
	}
	public XmlWebApplicationContext getApplicationContext() {
		return applicationContext;
	}
	
	/**
	 * 向spring的beanFactory动态地装载bean
	 * @param configLocationString 要装载的bean所在的xml配置文件位置。
	 */
	public void loadBean(String configLocationString){
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader((BeanDefinitionRegistry)getApplicationContext().getBeanFactory());
		beanDefinitionReader.setResourceLoader(getApplicationContext());
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(getApplicationContext()));
		try {
			String[] configLocations = new String[]{configLocationString};
			for(int i=0;i<configLocations.length;i++){
				System.out.println(configLocations[i]);
				beanDefinitionReader.loadBeanDefinitions(getApplicationContext().getResources(configLocations[i]));
			}	
		} catch (BeansException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

 

InitDataSource类(如果在系统运行的时候需要动态增加数据源的话可以调用此类来执行):

package com.wfy.init;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;


import com.wfy.multiData.DynamicLoadBean;

public class InitDataSource implements ApplicationContextAware {
	private ApplicationContext applicationContext = null;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.applicationContext = applicationContext;
	}

	public void init(String url){
		DynamicLoadBean dynamicLoadBean = (DynamicLoadBean)this.applicationContext.getBean("DynamicLoadBean");
		dynamicLoadBean.loadBean("classpath:dataSource/"+ url +".xml");
	}
}

 这样基本数据源配置就算完成了,在系统运行当时就可以根据不同的域名(连接地址)来动态的切换数据库了。

 

另外我的数据源配置文件是单独保存在一个文件夹当中的,这样服务在启动的时候就会动态的去加载这些数据源了

比如:

我的数据源文件是存放处classpath:dataSource文件夹里面,

那我的文件夹下面就会有很多的类似文件名的xml文件:_192.168.234.250.xml,_dataSource.xml(必须配置,默认数据源文件),_wfyerp.gnway.net.xml,然后在系统启动的时候就会去动态加载这些文件了。

如:当我们访问的地址是:http://wfyerp.gnway.net 这个网址的时候,实际数据库访问的将会是_wfyerp.gnway.net.xml文件中的数据源。

现在贴上这些配置文件的内容:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
	<bean id="_wfyerp.gnway.net" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"></property>
		<property name="url" value="jdbc:sqlserver://192.168.234.234:1433;databaseName=fy_erp_v6"></property>
		<property name="username" value="sa"></property>
		<property name="password" value="wfyerp2005"></property>
	</bean>
</beans>

 

然后会了能让spring在启动的时候加载这些配置文件,我们需要修改web.xml文件来让他启动的时候加载他们

web.xml修改spring配置如下:

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationContext.xml, (classpath:dataSource/*.xml 需要新增的配置信息)</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

 

文件内打括号的部分为新增的配置信息,其他跟默认不改变即可。

 

到此我们通过spring+过滤器 来动态切换数据源的问题就得到圆满解决了。

 

另附样例一个,导入spring2.5的包,然后index.jsp是演示不同数据源的效果,大家稍加改用几个,需要修改的地方有:

数据库连接,已经域名地址,如我的ip地址为:192.168.234.250 所以我的数据源配置文件就包含了这个文件,然后包含了localhost文件,这样就能看出数据源切换的效果来了。

5
1
分享到:
评论
9 楼 jinyanhui2008 2012-11-09  
hzq20100521 写道
你好,我的需求这个有点不一样,我的是系统启动的时候是连接的默认数据库,然后用户登录的时候,去判断这个用户应该连接的数据库,进行切换,网上搜了不少资料发现都没有可行,不晓得博主有没有做过这样的需求,我的邮箱francis.hu.0115@gmail.com,谢谢,静听佳音

我写的是可行的,甚至如果你在用户请求的时候动态创建数据库连接都没问题,只要你能忍受那个速度。
我写的其实是系统启动的时候没有连接任何数据库,但是在用户访问的时候才动态分配用户去连接那个数据库。
我想你的场景是否是使用默认数据库为系统初始化一些数据然后在切换数据库呢?其实这个完全是可以实现的,在MultiDataSource里面做些改造就能实现。
8 楼 hzq20100521 2012-10-30  
你好,我的需求这个有点不一样,我的是系统启动的时候是连接的默认数据库,然后用户登录的时候,去判断这个用户应该连接的数据库,进行切换,网上搜了不少资料发现都没有可行,不晓得博主有没有做过这样的需求,我的邮箱francis.hu.0115@gmail.com,谢谢,静听佳音
7 楼 jinyanhui2008 2012-02-22  
赵武艺 写道
您好,如果在系统运行时动态增加数据源,怎么调用InitDataSource这个类,是交给spring,还是new 一个InitDataSource的实例?谢谢!

不需要new一个的。
6 楼 赵武艺 2012-02-22  
您好,如果在系统运行时动态增加数据源,怎么调用InitDataSource这个类,是交给spring,还是new 一个InitDataSource的实例?谢谢!
5 楼 JetMah 2011-01-17  
如果是相同站点的话,不同数据源用户的Session怎样分开?现在这样的话是共用同样的Session的。
4 楼 anckey 2009-10-12  
谢谢,有项目用到,不过也是根据需求作出修改。。
3 楼 gotohometown 2009-08-26  
感谢,我也有类似的需求,通过session来判断具体的数据源。
先研究下
2 楼 jinyanhui2008 2009-07-17  
linvar 写道
不错,刚好有这个需求.
不过我的是根据不同的客户端
访问不同的数据源.

如果这样子的话可以通过session来判断,不同的session来调用不同的数据源,因为我的项目是flex的,并不存在session这个概念,所以只能那么使用了
1 楼 linvar 2009-07-06  
不错,刚好有这个需求.
不过我的是根据不同的客户端
访问不同的数据源.

相关推荐

Global site tag (gtag.js) - Google Analytics