浏览 19620 次
精华帖 (0) :: 良好帖 (3) :: 新手帖 (11) :: 隐藏帖 (3)
|
|
---|---|
作者 | 正文 |
发表时间:2010-01-07
最后修改:2010-01-07
请参考: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; } 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-01-08
最后修改:2010-01-08
“因为系统需要,有多个表空间”——可以在hbm文件中配置一个schema哦,通过不同的schema来访问不同位置的表就可以了,oracle还可以利用别名!
这样的话就不存在问题了哦 |
|
返回顶楼 | |
发表时间:2010-01-09
我曾经使用过上面的方法,两个dataSource,两个sessionFactory,两个hibernateTemplate,只需要DAO实现类不继承HibernateDaoTemplate,而是直接注入HibernateTemplate,就如下面的格式:
@Resource /*@Resource默认按照对象名注入(@Autowired默认按照类型注入,使用会报HiberanteTemplate有两个对象不知道注入哪一个的问题)*/ private HibernateTemplate hibernateTemplate1; @Resource private HibernateTemplate hibernateTemplate2; Spring会根据变量名自动将xml文件中的hibernateTemplate1和hibernateTemplate2注入到相应的对象中, junit测试可以成功从一个数据库中取出数据并保存到另外一个数据库中,唯一的问题就是,它们运行在两个不同的事务中,但是启动JTA分布式事务,又会有新的问题 |
|
返回顶楼 | |
发表时间:2010-01-11
呵呵,你这样做太麻烦了,其实很简单的
|
|
返回顶楼 | |
发表时间:2010-01-11
来个简单点的
<bean id="SystemDataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${system.db.driver}" /> <property name="url" value="${system.db.url}" /> <property name="username" value="${system.db.username}" /> <property name="password" value="${system.db.password}" /> <property name="maxActive" value="50"/> <property name="maxIdle" value="3"/> <property name="maxWait" value="1000"/> <property name="initialSize" value="10"/> </bean> <bean id="VendorDataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${vendor.db.driver}" /> <property name="url" value="${vendor.db.url}" /> <property name="username" value="${vendor.db.username}" /> <property name="password" value="${vendor.db.password}" /> <property name="maxActive" value="50"/> <property name="maxIdle" value="3"/> <property name="maxWait" value="1000"/> <property name="initialSize" value="10"/> </bean> <bean id='SystemJdbcTemplate' class='org.springframework.jdbc.core.JdbcTemplate'> <property name="dataSource" ref="SystemDataSource"/> </bean> <bean id='VedorJdbcTemplate' class='org.springframework.jdbc.core.JdbcTemplate'> <property name="dataSource" ref="SystemDataSource"/> </bean> 使用SystemJdbcTemplate和VedorJdbcTemplate就可以用不同的数据源 |
|
返回顶楼 | |
发表时间:2010-01-11
niyong 写道 呵呵,你这样做太麻烦了,其实很简单的
您有更好的办法吗 呵呵 请教一下 |
|
返回顶楼 | |
发表时间:2010-01-12
这种方式如果连接的是2个数据库时,要考虑用全局事务管理,不然有隐患的。先回再看
|
|
返回顶楼 | |
发表时间:2010-01-13
wuhoufeng 写道 这种方式如果连接的是2个数据库时,要考虑用全局事务管理,不然有隐患的。先回再看
我也考虑过这个问题,但是如果 只是从一个数据库查数据,然后到另一个数据库里插入和更新 就没什么问题。。如果真要把两个数据的操作放到一个事物里,我真还没想到好的办法,希望朋友能指点下! |
|
返回顶楼 | |
发表时间:2010-01-14
通过schema访问:
1 你的不同位置指的是其他用户的表吧!首先grant此表需要的权限给你的用户 2 在hbm文件配置schemal <hibernate-mapping schema="ownerOfTable"> 虽然登录的是otherUser但是 生成的sql是select ... from ownerOfTable.table where ... 第二种办法可以建立同义词: oracle的同义词 就是一种映射关系 同义词拥有如下好处:节省大量的数据库空间,对不同用户的操作同一张表没有多少差别;扩展的数据库的使用范围,能够在不同的数据库用户之间实现无缝交互;同义词可以创建在不同一个数据库服务器上,通过网络实现连接。 呵呵,oracle里面还有datalink可以解决数据库实例在不同物理机上的问题! good luck! ^ ^ |
|
返回顶楼 | |
发表时间:2010-01-20
要处理两个事物太麻烦,我这边量表多个数据库和表一般都采用JDBC的来连接
|
|
返回顶楼 | |