论坛首页 Java企业应用论坛

动态切换多数据源

浏览 47929 次
该帖已经被评为良好帖
作者 正文
   发表时间:2007-03-11  
spring2.0.1提供了Dynamic DataSource Routing,http://blog.interface21.com/main/2007/01/23/dynamic-datasource-routing/
0 请登录后投票
   发表时间: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);
}
0 请登录后投票
   发表时间:2007-04-04  
创建多个spring配置文件即可解决此问题。每个配置文件创建自己sessionfaction
0 请登录后投票
   发表时间:2007-04-04  
请楼上可以举例说明吗.

你所说的多个sessionfactory是指hibernate的session工厂吗,另外,如果
是多个配置文件,该如何加载,然后如何得到对应的上下文呢。不同的配置文件
中的bean名是否相同?
0 请登录后投票
   发表时间:2007-04-06  
1.每个数据源对应一个spring applicationContext 配置文件.
2.每个spring配置文件引用数据源不一样,其他的所有bean一样。
3.application server 启动的时候就加载spring 的所有配置文件,创建多个BeanFactory.并且把每个数据源的beanfaction和具体数据源建立一个对应关系,如:key(数据源标识),value(beanfaction),并且把创建的beanfaction 存储在application server的Application 对象里。
4.身下的就和spring用单数据源来处理一样了。
0 请登录后投票
   发表时间:2007-04-17  
谢谢楼上的回覆。

根据你的说明,我觉得还是可行的,不过发现有些不解的地方。
1.按照你的说法,所有的bean都有多套,大型应用中bean的数量是很多的,那是否合适呢。
2.在spring web中,如何能够分别加载多个配置文件到不同的beanFactory.目前,好像通用的配置都是将多个配置加载到系统唯一的beanFactory.我当时做项目的时候就是无法在spring web应用的环境中,去配置多个
beanFactory,所以采用了其他方法。

受你启发,也许可以在web中采用通用的方式配置,中间层则采用多个beanFactory提供前端选择,web层根据UI的选择来判断是调用哪个beanFactory来做后台处理。web部分的配置与中间层的配置也分开。
0 请登录后投票
   发表时间:2007-04-18  
oisiv 写道
TO: knight6892
谢谢你的回覆,我觉得你的方法和代码应该是目前动态数据源在spring中最好的实现了.
不过不知道数据源在切换中性能是否有影响,本来按照我的想法,sessionFactory是有多个,然后动态切换sessionFactory.但也许不可行,目前我使用自己的方法测试过查询数据,没有测试有关事务方面的操作.估计是不行.

接下来使用你的方法,进行测试,稍后会继续发贴.


我想knight6892的方法应该可行。但是你说有多个sessionFactory,然后去切换sessionFactory,我觉得这样是不是麻烦了,这里要求的本来就是切换datasource。

quaff的方法是要spring2.01的,如果采用spring1.x 相信knight6892的方法还是最值得推荐的。
0 请登录后投票
   发表时间:2007-04-19  
TO : spiritfrog

实际上我采用knight6892的方法测试过,实际是不可行的,具体体现在并发的问题上。多个用户都去动态切换数据源,那么得到的数据有可能不是来自你想得到的那个数据库。

说到根本的一点,还是需要用threadlocal来保存当前线程中的连接。这里的连接具体就应该是对应spring所用到的sessionFactory.因为spring的数据操作,以及事务操作都是建立在sessionFactory上的。我觉得在sessionFactory这个层面上来切换更合理一些。
0 请登录后投票
   发表时间:2007-04-19  
如果,数据库随着用户购买一套服务,动态增加一个数据库(数据库里面,表结构都是一样的).如上面所说的,通过设置多个datasource.我想这样行不通的.我目前配置解决方法,就是配置就预设一个datasource,如果用户登录进来以后,我们根据用户企业ID,动态连接到对应datasource.这样做法好处,无论你增加了多少个数据源,我们应用配置文件不需要修改.也不用重启服务器.目前还有一个问题没解决.就是事务怎么能让spring管理.
0 请登录后投票
   发表时间:2007-04-19  
多数据库应用需要交流,请联系我ssuupv@163.com.
对了我们目前的应用数据库,预期会达到20000左右
0 请登录后投票
论坛首页 Java企业应用版

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