锁定老帖子 主题:动态切换多数据源
该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间: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.请众位大大指点. 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间: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);; } } 这种方法不知是否可行? 线程安全的问题该如何解决呢? |
|
返回顶楼 | |
发表时间:2006-07-03
刚刚看到一篇文章,
开发线程安全的Spring Web应用 http://www.javafan.net/article/20050814100024212.html 从这篇文章,我觉得HibernateDaoSupport应该是线程安全的,即使DAO对象是单例.不知道是不是这个意思? 另外,文章中说到,在事务管理器每次执行时,会把Hibernate Session绑定到当前线程,这个时刻应该是在调用DAO对象前,因为事务是针对业务方法设定的. 这样就有些麻烦了,再在DAO中进行新的sessionFactory设定已经失去意义了.不知道是不是这种情况,还需测试一下. [/url] |
|
返回顶楼 | |
发表时间:2006-07-04
做一个FactoryBean, dao让factory在运行中动态返回嘛。
|
|
返回顶楼 | |
发表时间:2006-07-04
dwangel
不是很清楚你的想法,能否详细说明.谢谢 由于现在采用spring,事务针对业务方法,而不是DAO,所以数据源的动态切换就有些麻烦阿. |
|
返回顶楼 | |
发表时间: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> |
|
返回顶楼 | |
发表时间: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被调用前就在程序中动态声明事务. |
|
返回顶楼 | |
发表时间:2006-07-07
哦,想成DAO接口不发生任何变化去了,这样Singleton是无法实现的.
|
|
返回顶楼 | |
发表时间:2006-07-11
通过改造sessionFactory不就得了!或者需要动态得程序性控制,那就像楼上所说的,得到个beanFactory自己去组装!
|
|
返回顶楼 | |
发表时间: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 |
|
返回顶楼 | |