锁定老帖子 主题:动态切换多数据源
该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2007-03-11
spring2.0.1提供了Dynamic DataSource Routing,http://blog.interface21.com/main/2007/01/23/dynamic-datasource-routing/
|
|
返回顶楼 | |
发表时间:2007-04-04
樓上的文章如果早出來,那就好多了,大家就不用這麼辛苦的解決這個多數據源的需求.
在spring1.2.8上,我自己處理了這個問題,方法和2.0中的方式差不多,都需要一個threadlocal來 保存線程中的指定數據源. 我參照源碼修改了spring的幾個類, 1.首先新增一個類,這個類將全局唯一的保持線程變量,變量的key是從UI傳過來的數據源的名稱. class: HibernateTemplateThreadLocal.java public class HibernateTemplateThreadLocal implements ApplicationContextAware{ private ApplicationContext ctx; private static ThreadLocal<HibernateTemplate> tdl_hibernateTemplate = new ThreadLocal<HibernateTemplate>(); private static ThreadLocal<String> tdl_curdb = new ThreadLocal<String>(); private static ThreadLocal<JdbcTemplate> tdl_jdbcTemplate = new ThreadLocal<JdbcTemplate>(); public void setCurSessionFactory(String curDB){ SessionFactory sf=(SessionFactory)this.ctx.getBean(curDB+BasicConstant.BEAN_SESSIONFACTORY_NAME_SUFFIX); HibernateTemplate ht = new HibernateTemplate(sf); tdl_hibernateTemplate.set(ht); tdl_curdb.set(curDB); } public void setCurJdbcTemplate(String curDB){ //TODO JdbcTemplate is there when need. DataSource ds=(DataSource)this.ctx.getBean(curDB+BasicConstant.BEAN_DATASOURCE_NAME_SUFFIX); JdbcTemplate jt=new JdbcTemplate(ds); tdl_jdbcTemplate.set(jt); } public void setCurBothSessionAndJdbc(String curDB){ SessionFactory sf=(SessionFactory)this.ctx.getBean(curDB+BasicConstant.BEAN_SESSIONFACTORY_NAME_SUFFIX); HibernateTemplate ht = new HibernateTemplate(sf); tdl_hibernateTemplate.set(ht); tdl_curdb.set(curDB); //TODO JdbcTemplate is there when need. DataSource ds=(DataSource)this.ctx.getBean(curDB+BasicConstant.BEAN_DATASOURCE_NAME_SUFFIX); JdbcTemplate jt=new JdbcTemplate(ds); tdl_jdbcTemplate.set(jt); } public static HibernateTemplate getCurHibernateTemplate(){ return tdl_hibernateTemplate.get(); } public static String getCurDB(){ return tdl_curdb.get(); } public static JdbcTemplate getCurJdbcTemplate(){ return tdl_jdbcTemplate.get(); } /* (non Javadoc) * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) */ public void setApplicationContext(ApplicationContext arg0) throws BeansException { this.ctx=arg0; } } 2.DAO支持類,該類將從線程變量中取得對應的數據源 class: HibernateDaoPartitionSupport.java public abstract class HibernateDaoPartitionSupport extends DaoSupport{ /** * @deprecated * Set the Hibernate SessionFactory to be used by this DAO. * Will automatically create a HibernateTemplate for the given SessionFactory. * @see #createHibernateTemplate * @see #setHibernateTemplate */ public final void setSessionFactory(SessionFactory sessionFactory) { // this.hibernateTemplate = createHibernateTemplate(sessionFactory); } /** * Create a HibernateTemplate for the given SessionFactory. * Only invoked if populating the DAO with a SessionFactory reference! * <p>Can be overridden in subclasses to provide a HibernateTemplate instance * with different configuration, or a custom HibernateTemplate subclass. * @param sessionFactory the Hibernate SessionFactory to create a HibernateTemplate for * @return the new HibernateTemplate instance * @see #setSessionFactory */ /* private HibernateTemplate createHibernateTemplate(SessionFactory sessionFactory) { return new HibernateTemplate(sessionFactory); }*/ /** * Return the Hibernate SessionFactory used by this DAO. */ public final SessionFactory getSessionFactory() { return HibernateTemplateThreadLocal.getCurHibernateTemplate().getSessionFactory(); } /** * Set the HibernateTemplate for this DAO explicitly, * as an alternative to specifying a SessionFactory. * @see #setSessionFactory */ public final void setHibernateTemplate(HibernateTemplate hibernateTemplate) { } /** * Return the HibernateTemplate for this DAO, * pre-initialized with the SessionFactory or set explicitly. */ public final HibernateTemplate getHibernateTemplate() { return HibernateTemplateThreadLocal.getCurHibernateTemplate(); } protected final void checkDaoConfig() { /* if (this.hibernateTemplate == null) { throw new IllegalArgumentException("sessionFactory or hibernateTemplate is required"); } */ } /** * Get a Hibernate Session, either from the current transaction or a new one. * The latter is only allowed if the "allowCreate" setting of this bean's * HibernateTemplate is true. * <p><b>Note that this is not meant to be invoked from HibernateTemplate code * but rather just in plain Hibernate code.</b> Either rely on a thread-bound * Session (via HibernateInterceptor), or use it in combination with * <code>releaseSession</code>. * <p>In general, it is recommended to use HibernateTemplate, either with * the provided convenience operations or with a custom HibernateCallback * that provides you with a Session to work on. HibernateTemplate will care * for all resource management and for proper exception conversion. * @return the Hibernate Session * @throws DataAccessResourceFailureException if the Session couldn't be created * @throws IllegalStateException if no thread-bound Session found and allowCreate false * @see #releaseSession * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean) * @see org.springframework.orm.hibernate3.HibernateInterceptor * @see org.springframework.orm.hibernate3.HibernateTemplate * @see org.springframework.orm.hibernate3.HibernateCallback */ protected final Session getSession() throws DataAccessResourceFailureException, IllegalStateException { return getSession(getHibernateTemplate().isAllowCreate()); } /** * Get a Hibernate Session, either from the current transaction or * a new one. The latter is only allowed if "allowCreate" is true. * <p><b>Note that this is not meant to be invoked from HibernateTemplate code * but rather just in plain Hibernate code.</b> Either rely on a thread-bound * Session (via HibernateInterceptor), or use it in combination with * <code>releaseSession</code>. * <p>In general, it is recommended to use HibernateTemplate, either with * the provided convenience operations or with a custom HibernateCallback * that provides you with a Session to work on. HibernateTemplate will care * for all resource management and for proper exception conversion. * @param allowCreate if a non-transactional Session should be created when no * transactional Session can be found for the current thread * @return the Hibernate Session * @throws DataAccessResourceFailureException if the Session couldn't be created * @throws IllegalStateException if no thread-bound Session found and allowCreate false * @see #releaseSession * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean) * @see org.springframework.orm.hibernate3.HibernateInterceptor * @see org.springframework.orm.hibernate3.HibernateTemplate * @see org.springframework.orm.hibernate3.HibernateCallback */ protected final Session getSession(boolean allowCreate) throws DataAccessResourceFailureException, IllegalStateException { return (!allowCreate ? SessionFactoryUtils.getSession(getSessionFactory(), false) : SessionFactoryUtils.getSession( getSessionFactory(), getHibernateTemplate().getEntityInterceptor(), getHibernateTemplate().getJdbcExceptionTranslator())); } /** * Convert the given HibernateException to an appropriate exception from the * <code>org.springframework.dao</code> hierarchy. Will automatically detect * wrapped SQLExceptions and convert them accordingly. * <p>Delegates to the <code>convertHibernateAccessException</code> * method of this DAO's HibernateTemplate. * <p>Typically used in plain Hibernate code, in combination with * <code>getSession</code> and <code>releaseSession</code>. * @param ex HibernateException that occured * @return the corresponding DataAccessException instance * @see #setHibernateTemplate * @see #getSession * @see #releaseSession * @see org.springframework.orm.hibernate3.HibernateTemplate#convertHibernateAccessException */ protected final DataAccessException convertHibernateAccessException(HibernateTemplate ht,HibernateException ex) { return ht.convertHibernateAccessException(ex); } /** * Close the given Hibernate Session, created via this DAO's SessionFactory, * if it isn't bound to the thread. * @deprecated in favor of releaseSession * @see #releaseSession */ protected final void closeSessionIfNecessary(Session session) { releaseSession(session); } /** * Close the given Hibernate Session, created via this DAO's SessionFactory, * if it isn't bound to the thread. * <p>Typically used in plain Hibernate code, in combination with * <code>getSession</code> and <code>convertHibernateAccessException</code>. * @param session Session to close * @see org.springframework.orm.hibernate3.SessionFactoryUtils#releaseSession */ protected final void releaseSession(Session session) { SessionFactoryUtils.releaseSession(session, getSessionFactory()); } } 現在自己的DAO類將繼承上面這個類. 3.現在很重要的是修改事務管理類,參照源碼,自己修改類,將事務管理類中有關取得當前數據源的部分改成 從當前線程中取得. class:CustomTransactionInterceptor.java public class CustomTransactionInterceptor extends CustomTransactionAspectSupport implements MethodInterceptor,ApplicationContextAware { /** * Comment for <code>serialVersionUID</code> */ private static final long serialVersionUID = -9097662936755158399L; private ApplicationContext ctx; /** * Check that required properties were set. */ public void afterPropertiesSet() { if (this.transactionAttributeSource == null) { throw new IllegalArgumentException( "Either 'transactionAttributeSource' or 'transactionAttributes' is required: " + "If there are no transactional methods, don't use a CustomTransactionInterceptor " + "or TransactionProxyFactoryBean."); } } /* (non Javadoc) * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) */ public void setApplicationContext(ApplicationContext arg0) throws BeansException { this.ctx=arg0; } public PlatformTransactionManager getCurTransactionManager(){ String curDB=HibernateTemplateThreadLocal.getCurDB(); if(curDB==null) return null; PlatformTransactionManager ptm=(PlatformTransactionManager)this.ctx.getBean( curDB+BasicConstant.BEAN_TRANSACTIONMANAGER_NAME_SUFFIX); return ptm; } public Object invoke(MethodInvocation invocation) throws Throwable { // Work out the target class: may be <code>null</code>. // The TransactionAttributeSource should be passed the target class // as well as the method, which may be from an interface Class targetClass = (invocation.getThis() != null) ? invocation.getThis().getClass() : null; // Create transaction if necessary. TransactionInfo txInfo = createTransactionIfNecessary(invocation.getMethod(), targetClass); Object retVal = null; try { // This is an around advice. // Invoke the next interceptor in the chain. // This will normally result in a target object being invoked. retVal = invocation.proceed(); } catch (Throwable ex) { // target invocation exception doCloseTransactionAfterThrowing(txInfo, ex); throw ex; } finally { doFinally(txInfo); } doCommitTransactionAfterReturning(txInfo); return retVal; } } class: CustomTransactionAspectSupport.java public class CustomTransactionAspectSupport implements InitializingBean, Serializable { /** * Comment for <code>serialVersionUID</code> */ private static final long serialVersionUID = -7810366790730097705L; /** * Holder to support the <code>currentTransactionStatus()</code> method, * and to support communication between different cooperating advices * (e.g. before and after advice) if the aspect involves more than a * single method (as will be the case for around advice). */ private static ThreadLocal currentTransactionInfo = new ThreadLocal(); /** * Return the transaction status of the current method invocation. * Mainly intended for code that wants to set the current transaction * rollback-only but not throw an application exception. * @throws NoTransactionException if the transaction info cannot be found, * because the method was invoked outside an AOP invocation context */ public static TransactionStatus currentTransactionStatus() throws NoTransactionException { return currentTransactionInfo().transactionStatus; } /** * Subclasses can use this to return the current TransactionInfo. * Only subclasses that cannot handle all operations in one method, * such as an AspectJ aspect involving distinct before and after advice, * need to use this mechanism to get at the current TransactionInfo. * An around advice such as an AOP Alliance MethodInterceptor can hold a * reference to the TransactionInfo throughout the aspect method. * <p>A TransactionInfo will be returned even if no transaction was created. * The <code>TransactionInfo.hasTransaction()</code> method can be used to query this. * <p>To find out about specific transaction characteristics, consider using * TransactionSynchronizationManager's <code>isSynchronizationActive()</code> * and/or <code>isActualTransactionActive()</code> methods. * @return TransactionInfo bound to this thread * @throws NoTransactionException if no transaction has been created by an aspect * @see TransactionInfo#hasTransaction() * @see org.springframework.transaction.support.TransactionSynchronizationManager#isSynchronizationActive() * @see org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive() */ protected static TransactionInfo currentTransactionInfo() throws NoTransactionException { TransactionInfo info = (TransactionInfo) currentTransactionInfo.get(); if (info == null) { throw new NoTransactionException("No transaction aspect-managed TransactionStatus in scope"); } return info; } /** * Transient to avoid serialization. Not static as we want it * to be the correct logger for subclasses. Reconstituted in * readObject(). */ protected transient Log logger = LogFactory.getLog(getClass()); /** Delegate used to create, commit and rollback transactions */ protected PlatformTransactionManager transactionManager; /** Helper used to find transaction attributes */ protected TransactionAttributeSource transactionAttributeSource; /** * Set the transaction manager. This will perform actual * transaction management: This class is just a way of invoking it. */ public void setTransactionManager(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } /** * Return the transaction manager. */ public PlatformTransactionManager getTransactionManager() { return transactionManager; } /** * Set properties with method names as keys and transaction attribute * descriptors (parsed via TransactionAttributeEditor) as values: * e.g. key = "myMethod", value = "PROPAGATION_REQUIRED,readOnly". * <p>Note: Method names are always applied to the target class, * no matter if defined in an interface or the class itself. * <p>Internally, a NameMatchTransactionAttributeSource will be * created from the given properties. * @see #setTransactionAttributeSource * @see TransactionAttributeEditor * @see NameMatchTransactionAttributeSource */ public void setTransactionAttributes(Properties transactionAttributes) { NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource(); tas.setProperties(transactionAttributes); this.transactionAttributeSource = tas; } /** * Set the transaction attribute source which is used to find transaction * attributes. If specifying a String property value, a PropertyEditor * will create a MethodMapTransactionAttributeSource from the value. * @see TransactionAttributeSourceEditor * @see MethodMapTransactionAttributeSource * @see NameMatchTransactionAttributeSource * @see AttributesTransactionAttributeSource * @see org.springframework.transaction.annotation.AnnotationTransactionAttributeSource */ public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) { this.transactionAttributeSource = transactionAttributeSource; } /** * Return the transaction attribute source. */ public TransactionAttributeSource getTransactionAttributeSource() { return transactionAttributeSource; } /** * Check that required properties were set. */ public void afterPropertiesSet() { if (this.transactionManager == null) { throw new IllegalArgumentException("transactionManager is required"); } if (this.transactionAttributeSource == null) { throw new IllegalArgumentException( "Either 'transactionAttributeSource' or 'transactionAttributes' is required: " + "If there are no transactional methods, don't use a TransactionInterceptor " + "or TransactionProxyFactoryBean."); } } /** * Create a transaction if necessary. * @param method method about to execute * @param targetClass class the method is on * @return a TransactionInfo object, whether or not a transaction was created. * The hasTransaction() method on TransactionInfo can be used to tell if there * was a transaction created. */ protected TransactionInfo createTransactionIfNecessary(Method method, Class targetClass) { // If the transaction attribute is null, the method is non-transactional. final TransactionAttribute sourceAttr = this.transactionAttributeSource.getTransactionAttribute(method, targetClass); TransactionAttribute txAttr = sourceAttr; // If no name specified, apply method identification as transaction name. if (txAttr != null && txAttr.getName() == null) { final String name = methodIdentification(method); txAttr = new DelegatingTransactionAttribute(sourceAttr) { public String getName() { return name; } }; } TransactionInfo txInfo = new TransactionInfo(txAttr, method); if (txAttr != null) { // We need a transaction for this method if (logger.isDebugEnabled()) { logger.debug("Getting transaction for " + txInfo.joinpointIdentification()); } // The transaction manager will flag an error if an incompatible tx already exists txInfo.newTransactionStatus(getCurTransactionManager().getTransaction(txAttr)); } else { // The TransactionInfo.hasTransaction() method will return // false. We created it only to preserve the integrity of // the ThreadLocal stack maintained in this class. if (logger.isDebugEnabled()) logger.debug("Don't need to create transaction for [" + methodIdentification(method) + "]: this method isn't transactional"); } // We always bind the TransactionInfo to the thread, even if we didn't create // a new transaction here. This guarantees that the TransactionInfo stack // will be managed correctly even if no transaction was created by this aspect. txInfo.bindToThread(); return txInfo; } /** * Convenience method to return a String representation of this Method * for use in logging. Can be overridden in subclasses to provide a * different identifier for the given method. * @param method the method we're interested in * @return log message identifying this method * @see org.springframework.util.ClassUtils#getQualifiedMethodName */ protected String methodIdentification(Method method) { return ClassUtils.getQualifiedMethodName(method); } /** * Execute after successful completion of call, but not * after an exception was handled. * Do nothing if we didn't create a transaction. * @param txInfo information about the current transaction */ protected void doCommitTransactionAfterReturning(TransactionInfo txInfo) { if (txInfo != null && txInfo.hasTransaction()) { if (logger.isDebugEnabled()) { logger.debug("Invoking commit for transaction on " + txInfo.joinpointIdentification()); } getCurTransactionManager().commit(txInfo.getTransactionStatus()); } } /** * Handle a throwable, closing out the transaction. * We may commit or roll back, depending on our configuration. * @param txInfo information about the current transaction * @param ex throwable encountered */ protected void doCloseTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) { if (txInfo.hasTransaction()) { if (txInfo.transactionAttribute.rollbackOn(ex)) { if (logger.isDebugEnabled()) { logger.debug("Invoking rollback for transaction on " + txInfo.joinpointIdentification() + " due to throwable [" + ex + "]"); } try { getCurTransactionManager().rollback(txInfo.getTransactionStatus()); } catch (RuntimeException ex2) { logger.error("Application exception overridden by rollback exception", ex); throw ex2; } catch (Error err) { logger.error("Application exception overridden by rollback error", ex); throw err; } } else { // We don't roll back on this exception. if (logger.isDebugEnabled()) { logger.debug(txInfo.joinpointIdentification() + " threw throwable [" + ex + "] but this does not force transaction rollback"); } // Will still roll back if TransactionStatus.isRollbackOnly() is true. try { getCurTransactionManager().commit(txInfo.getTransactionStatus()); } catch (RuntimeException ex2) { logger.error("Application exception overridden by commit exception", ex); throw ex2; } catch (Error err) { logger.error("Application exception overridden by commit error", ex); throw err; } } } } /** * Call this in all cases: exception or normal return. Resets * the TransactionInfo ThreadLocal * @param txInfo information about the current transaction. May be <code>null</code>. */ protected void doFinally(TransactionInfo txInfo) { if (txInfo != null) { txInfo.restoreThreadLocalStatus(); } } //--------------------------------------------------------------------- // Serialization support //--------------------------------------------------------------------- private void readObject(ObjectInputStream ois) throws IOException { // Rely on default serialization, just initialize state after deserialization. try { ois.defaultReadObject(); } catch (ClassNotFoundException ex) { throw new AspectException("Failed to deserialize Spring AOP transaction aspect:" + "Check that Spring AOP libraries are available on the client side", ex); } // Initialize transient fields this.logger = LogFactory.getLog(getClass()); } public PlatformTransactionManager getCurTransactionManager(){ return null; } /** * Opaque object used to hold Transaction information. Subclasses * must pass it back to methods on this class, but not see its internals. */ protected class TransactionInfo { private final TransactionAttribute transactionAttribute; // TODO: Could open up to other kinds of joinpoint? private final Method method; private TransactionStatus transactionStatus; private TransactionInfo oldTransactionInfo; public TransactionInfo(TransactionAttribute transactionAttribute, Method method) { this.transactionAttribute = transactionAttribute; this.method = method; } /** * @return whether a transaction was created by this aspect, * or whether we just have a placeholder to keep ThreadLocal * stack integrity */ public boolean hasTransaction() { return transactionStatus != null; } /** * Return a String representation of this joinpoint (usually a Method call) * for use in logging. */ public String joinpointIdentification() { return methodIdentification(this.method); } public void newTransactionStatus(TransactionStatus status) { this.transactionStatus = status; } private void bindToThread() { // Expose current TransactionStatus, preserving any existing transactionStatus for // restoration after this transaction is complete. oldTransactionInfo = (TransactionInfo) currentTransactionInfo.get(); currentTransactionInfo.set(this); } private void restoreThreadLocalStatus() { // Use stack to restore old transaction TransactionInfo. // Will be <code>null</code> if none was set. currentTransactionInfo.set(oldTransactionInfo); } public TransactionStatus getTransactionStatus() { return this.transactionStatus; } public TransactionAttribute getTransactionAttribute() { return this.transactionAttribute; } } 4.現在來看配置,數據源和事務的bean的名稱都有一個特殊的前綴,該前綴將與UI的數據庫選項 保持一致,這樣用戶選擇了哪個數據庫,後台程序將會自動選擇哪個數據源或事務bean來處理. dataAccessContext.xml <bean id="OracleDs1_dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:OracleDS"/> </bean> <bean id="OracleDs2_dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:OracleDS1"/> </bean> <!-- Hibernate SessionFactory --> <bean id="hibernateMap" class="XXX.XXX.ListValueObj"> <property name="listValue"> <list> <value> XXXXXX/maps/XXX.hbm.xml </value> ... </list> </property> </bean> <bean id="OracleDs1_sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource"> <ref local="OracleDs1_dataSource" /> </property> <property name="mappingResources"> <ref local="hibernateMap"/> </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> <prop key="hibernate.jdbc.fetch_size">50</prop> <prop key="hibernate.jdbc.batch_size">30</prop> </props> </property> <property name="eventListeners"> <map> <entry key="merge"> <bean class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener" /> </entry> </map> </property> </bean> <bean id="OracleDs2_sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource"> <ref local="OracleDs2_dataSource" /> </property> <property name="mappingResources"> <ref local="hibernateMap"/> </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> <prop key="hibernate.jdbc.fetch_size">50</prop> <prop key="hibernate.jdbc.batch_size">30</prop> </props> </property> <property name="eventListeners"> <map> <entry key="merge"> <bean class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener" /> </entry> </map> </property> </bean> <bean id="hibernateTemplateThreadLocal" class="XXX.hibernate.HibernateTemplateThreadLocal"> </bean> <!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) --> <bean id="OracleDs1_transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="OracleDs1_sessionFactory" /> </bean> <bean id="OracleDs2_transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="OracleDs2_sessionFactory" /> </bean> <bean id="XXXXXDAO" class="XXXXXXXX.XXX.XXXXDAOImpl"> </bean> 數據源和事務管理器的名稱前綴是UI傳過來的數據源信息, class: ListValueObj.java public class ListValueObj extends ArrayList<Object> { /** * Comment for <code>serialVersionUID</code> */ private static final long serialVersionUID = 1L; public void setListValue(List<Object> list){ Iterator ite=list.iterator(); while(ite.hasNext()){ this.add(ite.next()); } } } 現在來看applicationContext.xml,這是一個公共的聲明性事務攔截器,針對所有名稱為*Service類型的 服務bean,將能產生事務,並進行管理. ... <!-- Transaction Interceptor --> <bean id="transactionInterceptor" class="XXXX.XXXX.CustomTransactionInterceptor" dependency-check="none" > <property name="transactionAttributes"> <props> <prop key="insert*">PROPAGATION_REQUIRED,-Exception</prop> <prop key="update*">PROPAGATION_REQUIRED,-Exception</prop> <prop key="delete*">PROPAGATION_REQUIRED,-Exception</prop> <prop key="*">PROPAGATION_REQUIRED,readOnly</prop> </props> </property> </bean> <bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator" dependency-check="none"> <property name="proxyTargetClass"> <value>true</value> </property> <property name="interceptorNames"> <list> <value>transactionInterceptor</value> </list> </property> <property name="beanNames"> <list> <value>*Service</value> </list> </property> </bean> ... 5.使用中,在調用服務bean前需要執行以下方法,然後去調用已經聲明了事務的服務類的對象 public void swapDataSource(String curDB){ hibernateTemplateThreadLocal.setCurSessionFactory(curDB); } |
|
返回顶楼 | |
发表时间:2007-04-04
创建多个spring配置文件即可解决此问题。每个配置文件创建自己sessionfaction
|
|
返回顶楼 | |
发表时间:2007-04-04
请楼上可以举例说明吗.
你所说的多个sessionfactory是指hibernate的session工厂吗,另外,如果 是多个配置文件,该如何加载,然后如何得到对应的上下文呢。不同的配置文件 中的bean名是否相同? |
|
返回顶楼 | |
发表时间:2007-04-06
1.每个数据源对应一个spring applicationContext 配置文件.
2.每个spring配置文件引用数据源不一样,其他的所有bean一样。 3.application server 启动的时候就加载spring 的所有配置文件,创建多个BeanFactory.并且把每个数据源的beanfaction和具体数据源建立一个对应关系,如:key(数据源标识),value(beanfaction),并且把创建的beanfaction 存储在application server的Application 对象里。 4.身下的就和spring用单数据源来处理一样了。 |
|
返回顶楼 | |
发表时间:2007-04-17
谢谢楼上的回覆。
根据你的说明,我觉得还是可行的,不过发现有些不解的地方。 1.按照你的说法,所有的bean都有多套,大型应用中bean的数量是很多的,那是否合适呢。 2.在spring web中,如何能够分别加载多个配置文件到不同的beanFactory.目前,好像通用的配置都是将多个配置加载到系统唯一的beanFactory.我当时做项目的时候就是无法在spring web应用的环境中,去配置多个 beanFactory,所以采用了其他方法。 受你启发,也许可以在web中采用通用的方式配置,中间层则采用多个beanFactory提供前端选择,web层根据UI的选择来判断是调用哪个beanFactory来做后台处理。web部分的配置与中间层的配置也分开。 |
|
返回顶楼 | |
发表时间:2007-04-18
oisiv 写道 TO: knight6892
谢谢你的回覆,我觉得你的方法和代码应该是目前动态数据源在spring中最好的实现了. 不过不知道数据源在切换中性能是否有影响,本来按照我的想法,sessionFactory是有多个,然后动态切换sessionFactory.但也许不可行,目前我使用自己的方法测试过查询数据,没有测试有关事务方面的操作.估计是不行. 接下来使用你的方法,进行测试,稍后会继续发贴. 我想knight6892的方法应该可行。但是你说有多个sessionFactory,然后去切换sessionFactory,我觉得这样是不是麻烦了,这里要求的本来就是切换datasource。 quaff的方法是要spring2.01的,如果采用spring1.x 相信knight6892的方法还是最值得推荐的。 |
|
返回顶楼 | |
发表时间:2007-04-19
TO : spiritfrog
实际上我采用knight6892的方法测试过,实际是不可行的,具体体现在并发的问题上。多个用户都去动态切换数据源,那么得到的数据有可能不是来自你想得到的那个数据库。 说到根本的一点,还是需要用threadlocal来保存当前线程中的连接。这里的连接具体就应该是对应spring所用到的sessionFactory.因为spring的数据操作,以及事务操作都是建立在sessionFactory上的。我觉得在sessionFactory这个层面上来切换更合理一些。 |
|
返回顶楼 | |
发表时间:2007-04-19
如果,数据库随着用户购买一套服务,动态增加一个数据库(数据库里面,表结构都是一样的).如上面所说的,通过设置多个datasource.我想这样行不通的.我目前配置解决方法,就是配置就预设一个datasource,如果用户登录进来以后,我们根据用户企业ID,动态连接到对应datasource.这样做法好处,无论你增加了多少个数据源,我们应用配置文件不需要修改.也不用重启服务器.目前还有一个问题没解决.就是事务怎么能让spring管理.
|
|
返回顶楼 | |
发表时间:2007-04-19
多数据库应用需要交流,请联系我ssuupv@163.com.
对了我们目前的应用数据库,预期会达到20000左右 |
|
返回顶楼 | |