论坛首页 Java企业应用论坛

动态切换多数据源

浏览 47932 次
该帖已经被评为良好帖
作者 正文
   发表时间:2006-07-03  
正在作的项目有以下需求:
在用户登陆介面有选择框,可以让用户选择使用哪个数据库,每个用户的选择也许不同,也就是说当前服务器上同时存在多个数据源的引用.
数据库的个数是可变的,但在系统运行后就固定了,如果需要增加,必须停止服务进行添加.

以往在另一个使用hibernate项目中,也实现了上面的功能,当时是注册多个sessionFactroy到jboss的不同的jndi,然后根据用户的选择,就可以选择使用哪个数据库.而且由于所有的事务都由自己控制,所以基本上是可行的.

现在采用spring+hibernate,事务由spring管理,不知是否可行.另外不知如何根据用户选择来切换到他要用的数据库上,而不影响别人.
以下是部分设置

application-context.xml
<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName"
			value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>

	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
<!--		<property name="mappingResources">
  			<value>petclinic.hbm.xml</value>
		</property>-->
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">
					${hibernate.dialect}
				</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.generate_statistics">true</prop>
			</props>
		</property>
		<property name="eventListeners">
			<map>
				<entry key="merge">
					<bean
						class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener" />
				</entry>
			</map>
		</property>
	</bean>

	<bean id="transactionManager"
		class="org.springframework.orm.hibernate3.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>

<bean id="dataSource1"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName"
			value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>

	<bean id="sessionFactory1"
		class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="dataSource1" ref="dataSource" />
<!--		<property name="mappingResources">
  			<value>petclinic.hbm.xml</value>
		</property>-->
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">
					${hibernate.dialect}
				</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.generate_statistics">true</prop>
			</props>
		</property>
		<property name="eventListeners">
			<map>
				<entry key="merge">
					<bean
						class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener" />
				</entry>
			</map>
		</property>
	</bean>

	<bean id="transactionManager1"
		class="org.springframework.orm.hibernate3.HibernateTransactionManager">
		<property name="sessionFactory1" ref="sessionFactory" />
	</bean>

 	<bean id="asyuserDAO" class="xxx.dao.hibernate.AsymodDAOImpl">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>    

AsymodDAOImpl.java
public class AsyuserDAOImpl extends HibernateDaoSupport implements AsyuserDAO {
public Asyuser getUserById(String userNo); {
		return (Asyuser); getHibernateTemplate();.get(Asyuser.class, userNo);;
	}
}

以上DAO实现中的sessionFactroy是由xml中动态注入的,但写死了,我如果想这个DAO从另一个数据库取资料,基本不可能.
能否去掉<property name="sessionFactory" ref="sessionFactory"/>
而在程序中根据用户选择而注入不同sessionFactory.
不过说到这里,又考虑到另一个问题,AsyuserDAOImpl 是单例,非线程安全的,sessionFactory这个属性也许会冲突,如果是这样,那么是不是让dao非单例呢.但这样创建对象的性能消耗是否很大.

就目前来说,我打算先使用这种方法,先不管性能如何,只希望不会有内存泄漏.我现在还不知道如何在程序中得到sessonFactory这个bean ,然后传给DAO.请众位大大指点.
   发表时间:2006-07-03  
刚刚思考了一下,有了进一步的想法.在DAO层添加BeanFactoryAware,很容易的得到BeanFactory,然后就可以得到指定的sessionFactory.
使用一个BaseDAO.java
public interface BaseDao extends BeanFactoryAware{

}

AsyuserDAO.java
public interface AsyuserDAO extends BaseDAO{
}

AsyuserDAOImpl.java
public class AsyuserDAOImpl extends HibernateDaoSupport implements AsyuserDAO {
public Asyuser getUserById(String userNo); {
		return (Asyuser); getHibernateTemplate();.get(Asyuser.class, userNo);;
	}

	public void setBeanFactory(BeanFactory arg0); throws BeansException {
		arg0.getBean("sessionFactory");;
		this.setSessionFactory((SessionFactory); arg0);;
	}
}


这种方法不知是否可行?
线程安全的问题该如何解决呢?
0 请登录后投票
   发表时间:2006-07-03  
刚刚看到一篇文章,
开发线程安全的Spring Web应用
http://www.javafan.net/article/20050814100024212.html

从这篇文章,我觉得HibernateDaoSupport应该是线程安全的,即使DAO对象是单例.不知道是不是这个意思?

另外,文章中说到,在事务管理器每次执行时,会把Hibernate Session绑定到当前线程,这个时刻应该是在调用DAO对象前,因为事务是针对业务方法设定的.
这样就有些麻烦了,再在DAO中进行新的sessionFactory设定已经失去意义了.不知道是不是这种情况,还需测试一下.
[/url]
0 请登录后投票
   发表时间:2006-07-04  
做一个FactoryBean, dao让factory在运行中动态返回嘛。
0 请登录后投票
   发表时间:2006-07-04  
dwangel
  不是很清楚你的想法,能否详细说明.谢谢

由于现在采用spring,事务针对业务方法,而不是DAO,所以数据源的动态切换就有些麻烦阿.
0 请登录后投票
   发表时间:2006-07-06  
dwangel 写道
做一个FactoryBean, dao让factory在运行中动态返回嘛。

可不可以让dataSource在FactoryBean中动态返回呢?还有这个FactoryBean里如何注入用户选择的参数呢.是不是这个意思,谢谢:
<bean id="userDao" class="test.Dao.UserDaoFactoryBean">
     <property name="daoImpls">
          <list>
             <bean>userDaoImpl1</bean>
             <bean>userDaoImpl2</bean>
          </list>
     </property>
</bean>
0 请登录后投票
   发表时间:2006-07-06  
关于Sessionfactory的动态切换,我已经实现了,在设置文件中设置好几个sessionFactory,其id是与用户选择的数据库标示相同.然后在dao中根据不同数据库从context中取回不同的sessionFactory.
我重新实现了HibernateDaoSupport这个类,代替spring的那个,用来封装切换.因为如果dao继承spring的这个类,在系统装载context时就必须要求强制注入一个sessionFactory给dao.但实际上,我的系统在装载context时根本不需要知道当前是连接哪个数据库.

具体可以参考http://www.jroller.com/page/thuss?entry=hibernate_mapping_one_class_to


另外,
但是,有关声明事务的问题,还是无法解决.在xml中,我是声明了bo的事务代理,但只能固定到某个sessionFactory.在DAO那里可以动态切换sessionFactory,但是在事务管理器这里,怎么动态切换呢,如何让bo的方法可以动态切换不同的事务.
我看到这里一篇文章,TransactionProxyFactoryBean的另类用法http://blog.matrix.org.cn/page/litaojian
是不是可以在bo被调用前就在程序中动态声明事务.
0 请登录后投票
   发表时间:2006-07-07  
哦,想成DAO接口不发生任何变化去了,这样Singleton是无法实现的.
0 请登录后投票
   发表时间:2006-07-11  
通过改造sessionFactory不就得了!或者需要动态得程序性控制,那就像楼上所说的,得到个beanFactory自己去组装!
0 请登录后投票
   发表时间:2006-07-21  
最近遇到多数据源的问题,找了好多资料一直没有找到很好的解决方法。
以下是自己利用ThreadLocal实现多数据源的方案.
利用DBCP的数据源来实现自己的数据源。
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
public class CommonDataSource implements DataSource {
    private Map dataSourceMap = null;
    public int getLoginTimeout() throws SQLException {
        return createDataSource().getLoginTimeout();
    }
    public void setLoginTimeout(int seconds) throws SQLException {
        createDataSource().setLoginTimeout(seconds);
    }
    public PrintWriter getLogWriter() throws SQLException {
        return createDataSource().getLogWriter();
    }
    public void setLogWriter(PrintWriter out) throws SQLException {
        createDataSource().setLogWriter(out);
    }
    public Connection getConnection() throws SQLException {
        return createDataSource().getConnection();
    }
    public Connection getConnection(String username, String password)
            throws SQLException {
        return createDataSource().getConnection(username, password);
    }
    /**
     * @return Returns the dataSourceMap.
     */
    public Map getDataSourceMap() {
        return dataSourceMap;
    }
    /**
     * @param dataSourceMap
     *            The dataSourceMap to set.
     */
    public void setDataSourceMap(Map dataSourceMap) {
        this.dataSourceMap = dataSourceMap;
    }
    protected BasicDataSource createDataSource() throws SQLException {
        String sp = (String) SpObserver.getSp();
        if (sp == null) {
            throw new SQLException(
                    "Cannot create datasource because of missing sp");
        }
        String dataSourceName = "dataSource" + sp;
        BasicDataSource dataSource = (BasicDataSource) dataSourceMap
                .get(dataSourceName);
        return dataSource;
    }

以下是SpObserver.java
public class SpObserver {
    private static ThreadLocal local=new ThreadLocal();
   
    public static void putSp(Object sp){
        local.set(sp);
    }
   
    public static Object getSp(){
        return local.get();
    }
}

前面用一个Filter来设置指定的数据源。

public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String dataSource = httpRequest.getParameter("dataSource");
        SpObserver.putSp(dataSource);
        chain.doFilter(request, response);
    }

有需要源码的可以与我联系wjw_319@hotmail.com
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics