2010.01.05——spring配置两个数据源

2010.01.05——spring配置两个数据源
请参考:http://www.iteye.com/topic/78432

因为系统需要,有多个表空间,所以要给spring配置多个数据源,我们是spring+hibernate的系统,估计

spring配置文件如下:
spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	     xmlns:aop="http://www.springframework.org/schema/aop"
	     xmlns:tx="http://www.springframework.org/schema/tx"
	     xmlns:context="http://www.springframework.org/schema/context"
	     xsi:schemaLocation="http://www.springframework.org/schema/beans 

http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://www.springframework.org/schema/aop 

http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx 

http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
            http://www.springframework.org/schema/context 

http://www.springframework.org/schema/context/spring-context-2.5.xsd"> 
	
	<!-- 数据库外部文件配置 -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<list><value>classpath:db.properties</value></list>
		</property>
		<property name="fileEncoding" value="utf-8" />
	</bean>  
    <!-- 数据库外部文件配置 -->
    
    <!-- 配置数据源 使用dbcp数据源 -->
    <bean id="dataSource1"  
        class="org.apache.commons.dbcp.BasicDataSource"  
        destroy-method="close">   
        <property name="driverClassName"  
            value="${jdbc.driverClassName1}" />   
        <property name="url" value="${jdbc.url1}" />   
        <property name="username" value="${jdbc.username1}" />   
        <property name="password" value="${jdbc.password1}" />   
    </bean>
    
    <!-- 配置数据源2 -->
    <bean id="dataSource2"  
        class="org.apache.commons.dbcp.BasicDataSource"  
        destroy-method="close">   
        <property name="driverClassName"  
            value="${jdbc.driverClassName2}" />   
        <property name="url" value="${jdbc.url2}" />   
        <property name="username" value="${jdbc.username2}" />   
        <property name="password" value="${jdbc.password2}" />   
    </bean>
	<!-- 配置数据源 使用dbcp数据源 -->
  
  	<!-- Hibernate SessionFactory配置 -->
    <bean id="sessionFactory1"  
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">   
        <property name="dataSource" ref="dataSource1" />   
        <property name="annotatedClasses">   
            <list>   
                <value>com.pojo.DT_RGNCD</value>
                
            </list>   
        </property>   
        <property name="hibernateProperties">   
            <props>   
                <prop key="hibernate.dialect">   
                    org.hibernate.dialect.OracleDialect   
                </prop>   
                <prop key="show_sql">true</prop>   
                <prop key="hibernate.format_sql">true</prop>   
                <prop key="hibernate.use_sql_comments">true</prop>   
            </props>   
        </property>   
    </bean>   
    <!-- sessionFactory2  -->
    <bean id="sessionFactory2"  
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">   
        <property name="dataSource" ref="dataSource2" />   
        <property name="annotatedClasses">   
            <list>   
                <value>com.pojo.ST_STBPRP_B</value>
                
            </list>   
        </property>   
        <property name="hibernateProperties">   
            <props>   
                <prop key="hibernate.dialect">   
                    org.hibernate.dialect.OracleDialect   
                </prop>   
                <prop key="show_sql">true</prop>   
                <prop key="hibernate.format_sql">true</prop>   
                <prop key="hibernate.use_sql_comments">true</prop>   
            </props>   
        </property>   
    </bean>
    <!-- Hibernate SessionFactory配置 -->
    
    
    <!-- Hibernate事务和hibernateTemplate -->
    <bean id="transactionManager1"  
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">   
        <property name="sessionFactory" ref="sessionFactory1" />   
    </bean>   
  	<bean id="hibernateTemplate1" 

class="org.springframework.orm.hibernate3.HibernateTemplate">  
	    <property name="sessionFactory" ref="sessionFactory1" />  
	</bean> 
	
	<bean id="transactionManager2"  
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">   
        <property name="sessionFactory" ref="sessionFactory2" />   
    </bean>   
  	<bean id="hibernateTemplate2" 

class="org.springframework.orm.hibernate3.HibernateTemplate">  
	    <property name="sessionFactory" ref="sessionFactory2" />  
	</bean>
	<!-- Hibernate事务和hibernateTemplate -->

    <!-- Aop 事务管理控制 -->
	<aop:config proxy-target-class="false">
		<!-- com.service.*.* 下的类的方法使用事务控制 -->
		<aop:advisor
			pointcut="execution(* com.service.*.*(..))"
			advice-ref="txAdvice" />
	</aop:config>
	<tx:advice id="txAdvice" transaction-manager="transactionManager1">
		<tx:attributes>
			<!-- get find等查询方法不使用事务 其他方法都使用事务控制,当发生异常时

,整个方法事务回滚 -->
			<tx:method name="get*" read-only="true" />
			<tx:method name="find*" read-only="true" />
			<tx:method name="*" propagation="REQUIRED"
				rollback-for="Exception" />
		</tx:attributes>
	</tx:advice>
	<!-- Aop 事务管理控制 --> 
	
	<!-- Dao -->
	<!-- query -->
	<bean id="DT_RGNCDDao" class="com.dao.imp.DT_RGNCDDaoImp">
		<property name="hibernateTemplate">
			<ref bean="hibernateTemplate1"/>
		</property>
	</bean>
	<!-- query -->
	<!-- query_su9921 -->
	<bean id="ST_STBPRP_BDao" class="com.dao.imp.ST_STBPRP_BDaoImp">
		<property name="hibernateTemplate">
			<ref bean="hibernateTemplate2"/>
		</property>
	</bean>
	<!-- query_su9921 -->
	
	
	<!-- Dao -->
	
	<!-- Service -->
	<bean id="yuliangService" class="com.service.imp.YuliangServiceImp">
		<property name="DT_RGNCDDao">
			<ref bean="DT_RGNCDDao"/>
		</property>
	</bean>
	<!-- Service -->
	
	
</beans>  
对了 db.properties的内容如下
#MySql
#jdbc.driverClassName=com.mysql.jdbc.Driver
#jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8
#jdbc.username=root
#jdbc.password=123456

#oracle
#query
jdbc.driverClassName1=oracle.jdbc.driver.OracleDriver
jdbc.url1=jdbc:oracle:thin:@192.168.0.173:1521:KHMS
jdbc.username1=query
jdbc.password1=query
#query_su9921
jdbc.driverClassName2=oracle.jdbc.driver.OracleDriver
jdbc.url2=jdbc:oracle:thin:@192.168.0.173:1521:KHMS
jdbc.username2=query_su9921
jdbc.password2=query_su9921


上面的spring.xml的配置还是有很多问题:
1.我要两个dataSource,就必须有两个SessionFactory,两个transactionManager,两个HibernateTample,这

样不管成不成功都是有问题的
2.这样子写,老是没法通过junit的测试,在网上也找了好多办法但是总是不行

所以,就想改进一下,在这个过程中我遇到最多的异常就是,唯一的dataSource,找到了两个,我就想,能

不能把两个dataSource弄成一个那,一下就是基于这个出发点写的


org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
Spring2.0.1以后的版本已经支持配置多数据源,并且可以在运行的时候动态加载不同的数据源。通过继承

AbstractRoutingDataSource就可以实现多数据源的动态转换。


CustomerContextHolder.java
/**
 * 把当前使用的数据源保存在当前线程中,放在contextHolder中
 * @author 谢 2010-1-5
 */
public class CustomerContextHolder {   
    private static final ThreadLocal contextHolder =    
        new ThreadLocal();   
       
    public static void setCustomerType(String customerType) {   
      contextHolder.set(customerType);   
    }   
       
    public static String getCustomerType() {   
      return (String) contextHolder.get();   
    }   
       
    public static void clearCustomerType() {   
      contextHolder.remove();   
    }
  
}


AbstractRoutingDataSource.java

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;   

/**
 * 继承自AbstractRoutingDataSource的类可以用来选着使用哪个数据源
 * 根据determineCurrentLookupKey返回的内容来判断
 * 当前的做法是把保存在线程中的CustomerContextHolder.getCustomerType()值来判断。
 * @author 谢 2010-1-5 
 */
public class DynamicDataSource extends AbstractRoutingDataSource {   
  
    protected Object determineCurrentLookupKey() {   
        return CustomerContextHolder.getCustomerType();   
    }
  
} 


spring.xml

<!-- 配置数据源 使用dbcp数据源 -->
    
    <!-- 配置数据源1 -->
    <bean id="query" class="org.apache.commons.dbcp.BasicDataSource">   
        <property name="driverClassName"  
            value="${jdbc.driverClassName1}" />   
        <property name="url" value="${jdbc.url1}" />   
        <property name="username" value="${jdbc.username1}" />   
        <property name="password" value="${jdbc.password1}" />   
    </bean>
    <!-- 配置数据源2 -->
    <bean id="query_su9921" class="org.apache.commons.dbcp.BasicDataSource">   
        <property name="driverClassName"  
            value="${jdbc.driverClassName2}" />   
        <property name="url" value="${jdbc.url2}" />   
        <property name="username" value="${jdbc.username2}" />   
        <property name="password" value="${jdbc.password2}" />   
    </bean>
    
    <bean id="dataSource" class="com.huitu.khms.util.DynamicDataSource">//这个就是我们上面自己	

								写的数据源
         <property name="targetDataSources">
            <map key-type="java.lang.String">
               <entry key="query" value-ref="query"/>
               <entry key="query_su9921" value-ref="query_su9921"/>
            </map>
         </property>
         <property name="defaultTargetDataSource" ref="query"/>//这个是默认加载那个数据源
    </bean>
       
	<!-- 配置数据源 使用dbcp数据源 -->

后面sessionFactory和transactionManager的配置还是一样的
<!-- Hibernate事务和hibernateTemplate -->
    <bean id="transactionManager"  
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">   
        <property name="sessionFactory" ref="sessionFactory" />   
    </bean>   
   <bean id="hibernateTemplate" 		

class="org.springframework.orm.hibernate3.HibernateTemplate">  
	    <property name="sessionFactory" ref="sessionFactory" />  
   </bean> 
<!-- Hibernate事务和hibernateTemplate -->

然后测试
@ContextConfiguration(locations={"classpath:spring.xml","classpath:mvc-				

servlet.xml"},inheritLocations=false) 
public class TestDao {
	
	private DT_RGNCDDao DT_RGNCDDao;
	
	private ST_RSVRFCCH_BDao ST_RSVRFCCH_BDao;
	
	YuliangService yuliangService;
	
	@Autowired
	public void setDataSource(@Qualifier("dataSource") DataSource dataSource) {
		super.setDataSource(dataSource);
	}
	
	@Test
	public void test3(){
		
		ST_RSVRFCCH_BDao = (ST_RSVRFCCH_BDao) ctx.getBean("ST_RSVRFCCH_BDao");
		CustomerContextHolder.setCustomerType("query_su9921");
		DT_RGNCDDao = (DT_RGNCDDao) ctx.getBean("DT_RGNCDDao");				
		System.out.println(ST_RSVRFCCH_BDao.findAll().size());
		CustomerContextHolder.setCustomerType("query");
		System.out.println(DT_RGNCDDao.findAll().size());
	}
}
这样测试时,就遇到一个问题,每次总是只打印一个,换句话说,就是这个datasource没有切换,这个问题

花了好长时间,最后不用spring的@ContextConfiguration,如下
public class TestCase {
public ApplicationContext ctx = null; 
	
	@Before
	public void setUp(){
		ctx = new ClassPathXmlApplicationContext(new String[]				

{"classpath:spring.xml","classpath:mvc-servlet.xml"});
	}
	
	@Test
	public void testService(){
		CustomerContextHolder.setCustomerType("query");
		DT_RGNCDDao DT_RGNCDDao = (DT_RGNCDDao) ctx.getBean("DT_RGNCDDao");
		System.out.println(DT_RGNCDDao.findAll().size());
		CustomerContextHolder.setCustomerType("query_su9921");
		ST_RSVRFCCH_BDao ST_RSVRFCCH_BDao = (ST_RSVRFCCH_BDao) ctx.getBean		

									("ST_RSVRFCCH_BDao");
		System.out.println(ST_RSVRFCCH_BDao.findAll().size());
	}
}

但是每次都写CustomerContextHolder.setCustomerType("query");是很麻烦的,所以应该配一个

BeforeAdvice

public class DaoAdvice implements MethodBeforeAdvice{

	public void before(Method method, Object[] args, Object obj)
			throws Throwable {
		String classSimpleName = obj.getClass().getSimpleName();
		String className = classSimpleName.substring(0, classSimpleName.length()-3);
		if(className.equals("ST_RSVRFCCH_BDao")){
			CustomerContextHolder.setCustomerType("query_su9921");
			System.out.println("------------ST_RSVRFCCH_BDao---------query_su9921-

----------------");
		}else if(className.equals("DT_RGNCDDao")){
			System.out.println("------------DT_RGNCDDao---------query-------------

----");
			CustomerContextHolder.setCustomerType("query");
		}
	}
	
}

这样就不用每次都写CustomerContextHolder了,spring.xml里面的配置如下:
<aop:config proxy-target-class="false">
		<aop:advisor
			pointcut="execution(* com.huitu.khms.dao..*.*(..))"
			advice-ref="daoAdvice" />
</aop:config>
	
<bean id="daoAdvice" class="com.huitu.khms.util.DaoAdvice">
		
</bean>



但是还有一个问题,就是当我调用service时,就只会出现一个了,我的sevice如下
public class YuliangServiceImp implements YuliangService{

	public void test() {
		System.out.println(DT_RGNCDDao.findAll().size());
		System.out.println(ST_RSVRFCCH_BDao.findAll().size());
	}

	public DT_RGNCDDao getDT_RGNCDDao() {
		return DT_RGNCDDao;
	}
	public void setDT_RGNCDDao(DT_RGNCDDao dao) {
		DT_RGNCDDao = dao;
	}
	public ST_RSVRFCCH_BDao getST_RSVRFCCH_BDao() {
		return ST_RSVRFCCH_BDao;
	}
	public void setST_RSVRFCCH_BDao(ST_RSVRFCCH_BDao dao) {
		ST_RSVRFCCH_BDao = dao;
	}
	private DT_RGNCDDao DT_RGNCDDao;
	private ST_RSVRFCCH_BDao ST_RSVRFCCH_BDao;
}

最后发现,当我把implements YuliangService去掉时,它就可以测试了没有错误了,这个问题一直很郁闷,

不知道原因,后来发现当我把spring.xml里面的事务去掉后,就可以正常运行了,
<!-- Aop 事务管理控制 -->
    <!-- 
	<aop:config proxy-target-class="false">
		<aop:advisor
			pointcut="execution(* com.dao.*.*(..))"
			advice-ref="txAdvice" />
	</aop:config>
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="get*" read-only="true" />
			<tx:method name="find*" read-only="true" />
			<tx:method name="*" propagation="REQUIRED"
				rollback-for="Exception" />
		</tx:attributes>
	</tx:advice>
	 -->
<!-- Aop 事务管理控制 -->
这个原因一直我也不是很明白,云里雾里的。。。。
上面那个BeforeAdvice还是有点问题,如果我的dao层方法很多呐,总不能全是if语句吧,所以,做了一下改


首先看spring.xml
<aop:config proxy-target-class="false">
		<aop:advisor
			pointcut="execution(* com.huitu.khms.dao..*.*(..))"
			advice-ref="daoAdvice" />
</aop:config>
	
<!-- 拦截所有的dao,根据配置自动判断使用数据源 -->
<bean id="daoAdvice" class="com.huitu.khms.util.DaoAdvice">
	<property name="dataSources">
		<map>
			<entry key="query">
				<value>dao1,dao2,dao3</value>
			</entry>
			<entry key="query_su9921">
				<value>dao11,dao22,dao33</value>
			</entry>
		</map>
	</property>
</bean>


再看这个BeforeAdvice

/**
 * 
 * 这个类拦截所有的dao的方法,根据dataSources中配置自动判断当前这个dao使用对应的数据源
 * @author 谢 2010-1-7
 *
 */
public class DaoAdvice implements MethodBeforeAdvice{

	public void before(Method method, Object[] args, Object obj)
			throws Throwable {
		String classSimpleName = obj.getClass().getSimpleName();
		//当前的dao的className
		String className = classSimpleName.substring(0, classSimpleName.length()-3);
		
		Set keys = dataSources.keySet();
		for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
			//dataSource数据源名称
			String dataSource = (String) iterator.next();
			//所有daos
			String daos = ","+dataSources.get(dataSource)+",";
			if(daos.indexOf(className)!=-1){
				//设置当前dao使用的数据源
				CustomerContextHolder.setCustomerType(dataSource);
				break;
			}
		}
	}
	
	public Map getDataSources() {
		return dataSources;
	}

	public void setDataSources(Map dataSources) {
		this.dataSources = dataSources;
	}
	/*
	 * key 是数据源
	 * value 是daos,使用key的数据库的dao
	 */
	private Map dataSources;
}

你可能感兴趣的:(spring,oracle,Hibernate,jdbc,配置管理)