码迷,mamicode.com
首页 > 数据库 > 详细

spring配置文件---之数据库切换

时间:2016-05-18 18:55:28      阅读:325      评论:0      收藏:0      [点我收藏+]

标签:

技术分享

技术分享

技术分享

技术分享

技术分享


spring 2.0.1中引入了AbstractRoutingDataSource, 该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上。

     Spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性。而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据。

 
Spring对于多数据源,以数据库表为参照,大体上可以分成两大类情况: 
一是,表级上的跨数据库。即,对于不同的数据库却有相同的表(表名和表结构完全相同)。 
二是,非表级上的跨数据库。即,多个数据源不存在相同的表。 
Spring2.x的版本中采用Proxy模式,就是我们在方案中实现一个虚拟的数据源,并且用它来封装数据源选择逻辑,这样就可以有效地将数据源选择逻辑从Client中分离出来。Client提供选择所需的上下文(因为这是Client所知道的),由虚拟的DataSource根据Client提供的上下文来实现数据源的选择。 

具体的实现就是,虚拟的DataSource仅需继承AbstractRoutingDataSource实现determineCurrentLookupKey()在其中封装数据源的选择逻辑。

<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd">


<!-- 只有一个数据为时,只需要配置一个bean:dataSource -->
<bean id="dataSource_default" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
   <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
   <property name="url"><value>jdbc:mysql://</value></property>
   <property name="username"><value></value></property>
   <property name="password"><value></value></property>
  
   <property name="initialSize"><value>2</value></property>  
   <property name="maxActive"><value>2</value></property>
   <property name="minIdle"><value>2</value></property>
   <property name="maxIdle"><value>2</value></property>  
   <property name="maxWait"><value>10000</value></property>
<property name= "testWhileIdle" ><value>true</value></property>
<property name= "testOnBorrow" ><value>true</value></property>
<property name= "testOnReturn" ><value>true</value></property>
<property name= "validationQuery" ><value>select 1 from dual</value></property>
<property name= "timeBetweenEvictionRunsMillis" ><value>30000</value></property>
<property name= "numTestsPerEvictionRun" ><value>32</value></property>
<property name="minEvictableIdleTimeMillis"><value>1800000</value></property>
<property name="removeAbandoned" ><value>true</value></property> 
<property name="removeAbandonedTimeout"><value>180</value></property>
</bean>
<bean id="tfj" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
   <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
   <property name="url"><value>jdbc:mysql://</value></property>
   <property name="username"><value></value></property>
   <property name="password"><value></value></property>
  
   <property name="initialSize"><value>2</value></property>  
   <property name="maxActive"><value>2</value></property>
   <property name="minIdle"><value>2</value></property>
   <property name="maxIdle"><value>2</value></property>  
   <property name="maxWait"><value>10000</value></property>
<property name= "testWhileIdle" ><value>true</value></property>
<property name= "testOnBorrow" ><value>true</value></property>
<property name= "testOnReturn" ><value>true</value></property>
<property name= "validationQuery" ><value>select 1 from dual</value></property>
<property name= "timeBetweenEvictionRunsMillis" ><value>30000</value></property>
<property name= "numTestsPerEvictionRun" ><value>32</value></property>
<property name="minEvictableIdleTimeMillis"><value>1800000</value></property>
<property name="removeAbandoned" ><value>true</value></property> 
<property name="removeAbandonedTimeout"><value>180</value></property>
</bean>
<bean id="dataSource" class="">
           <property name="targetDataSources">
    <map key-type="java.lang.String">
    <entry value-ref="dataSource_default" key="default"/> 
    </map>
        </property>
    <property name="defaultTargetDataSource" ref="dataSource_default">
        </property>
    </bean>
    
  //重要的类,他里面存的是动态数据配置文件
<bean id="dcCreateDataSourceBean" class="">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
<property name="dynamicDataSource" ref="dataSource"></property>
</bean> 


<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 用于异步队列更新数据库 -->
<bean id="partitionDBMapping" class="java.util.concurrent.ConcurrentHashMap">
<constructor-arg>
<map>
<entry value="default" key="databaseGroup" />
</map>
</constructor-arg>
</bean>


</beans>



下面是copy别人的

1 前言: 
  公司需要做一个分析统计系统,该系统需要连接N台服务器结点,进行数据的统计分析操作, 
项目是以spring为基础框架搭建的.收集现在网上的所有关于多数据源配置的方式,并没有自己十分满意的,例如我有N个数据源,按照现网可以搜索到的配置方式,都是在spring配置文件中配置N个datasource,并通过实现AbstractRoutingDataSource抽象类的子类进行多数据源的管理.这种情况个人认为很不合理,一来维护起来困难,二来,数据源的基本信息基本都一致的情况下,会造成配置文件重复性的文字.(比如:初始化连接数,最小连接数,最大连接数,等等通用的信息.) 
   
  而配置AbstractRoutingDataSource的子类必须进行targetDataSources属性的初始化,这也决定了如上所说的情况,如果有N个数据源的情况,会让配置文件显得非常冗长,也容易侵染其他业务bean配置.原因请看代码: 
AbstractRoutingDataSource.java 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void afterPropertiesSet() {
         if ( this .targetDataSources == null ) {
             throw new IllegalArgumentException( "Property ‘targetDataSources‘ is required" );
         }
         this .resolvedDataSources = new HashMap<Object, DataSource>( this.targetDataSources.size());
         for (Map.Entry entry : this .targetDataSources.entrySet()) {
             Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());
             DataSource dataSource = resolveSpecifiedDataSource(entry.getValue());
             this .resolvedDataSources.put(lookupKey, dataSource);
         }
         if ( this .defaultTargetDataSource != null ) {
             this .resolvedDefaultDataSource = resolveSpecifiedDataSource( this.defaultTargetDataSource);
         }
     }

如代码所示,AbstractRoutingDataSource实现了InitializingBean类,实现了afterPropertiesSet方法.afterPropertiesSet方法在bean的属性赋值之后执行, 
并检查targetDataSources 是否有值,如果有值就将targetDataSources  转换成 
resolvedDataSources.也就是说,如果你要使用AbstractRoutingDataSource,就必须在方法afterPropertiesSet执行之前,进行targetDataSources 属性的初始化.这也就是目前网上的配置方式,在配置文件里配置N个数据源的由来. 




笔者认为很不爽快,按着自己想法,多数据源管理应该满足如下条件: 
1 多数据源配置信息应该独立出来 
2 实现动态加载,即通过自定义配置文件,让系统动态加载数据源,而不是通过spring配置文件去配置. 
3 AbstractRoutingDataSource的子类无需配置datasource集合,只需要简单的通过bean标签,声明在配置文件里,或者仅仅需要一个@Component注解. 
4 实现动态切换数据源. 

2 实现: 
   spring为多数据源的支持提供了AbstractRoutingDataSource.java类. 
该类通过运行时指定当前的datasourcename来进行数据源的动态切换.您应该根据需要 
重写AbstractRoutingDataSource的几个方法 
AbstractRoutingDataSource.java 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//查找当前用户上下文变量中设置的数据源.
@Override
     protected Object determineCurrentLookupKey() {
         DataSourceType dataSourceType= DataSourceContextHolder.getDataSourceType();
         
         return dataSourceType;
     }
 
//设置默认的数据源
     @Override
     public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
         super .setDefaultTargetDataSource(defaultTargetDataSource);
     }
//设置数据源集合.
     @Override
     public void setTargetDataSources(Map targetDataSources) {
         super .setTargetDataSources(targetDataSources);
     }



DataSourceContextHolder.java 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
*数据源线程上下文对象.
*
*/
public class DataSourceContextHolder {
 
     private static final ThreadLocal contextHolder= new ThreadLocal();
     
     public static void setDataSourceType(DataSourceType dataSourceType){
         contextHolder.set(dataSourceType);
     }
     
     public static DataSourceType getDataSourceType(){
         return (DataSourceType) contextHolder.get();
     }
     
     public static void clearDataSourceType(){
         contextHolder.remove();
     }
     
}


有了以上两个基础类之后,我们还需要解决一个问题,如何让我们的数据源管理器(AbstractRoutingDataSource的子类)实现声明的时候零配置,即无需对targetDataSources(数据源集合)进行配置,而是采取系统初始化时加载的方式. 

思路: 
1 必须在afterPropertiesSet方法之前,将必须属性的值进行填充. 
2 必须把动态加载的数据源注册为spring容器内的bean. 

因此,为了实现以上2点需求,我们必须继承 AbstractRoutingDataSource类,并且实线 ApplicationContextAware 接口. 
MutiDataSourceBean.java 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/**
  * 初始化动态数据源
  * @author Administrator
  *
  */
/**
  * 初始化动态数据源
  * @author Administrator
  *
  */
@Component ( "mutiGameDs" )
public class MutiDataSourceBean extends AbstractRoutingDataSource implementsApplicationContextAware{
 
     private static Logger log = Logger.getLogger( "InistailizeMutiDataSourceBean" );
     private static ApplicationContext ac ;
 
     @Override
     public void afterPropertiesSet() {
         
         log.info( "初始化多数据源" );
         try {
             initailizeMutiDataSource();
         catch (Exception e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
         log.info( "多数据源加入spring容器中成功!" );
 
         super .afterPropertiesSet();
     }
 
     @Override
     public void setApplicationContext(ApplicationContext ctx)
             throws BeansException {
         // TODO Auto-generated method stub
         ac=ctx;
         
     }
 
 
     private  void initailizeMutiDataSource() throws Exception{
         
         Document doc  = XmlUtils.loadXMLClassPath( "game-ds.xml" );
         List servers = doc.selectNodes( "//server" );
         DruidDataSource ds = null ;
         .....
         ....
         ..
         .
         DefaultListableBeanFactory acf  = (DefaultListableBeanFactory)ac.getAutowireCapableBeanFactory();
         
         
         Map<Object,DruidDataSource> dsMap  = new HashMap<Object, DruidDataSource>();
         
         
         for (Object object : servers) {
             Element el  =(Element)object;
             ds = new DruidDataSource();
             String id = el.attributeValue( "id" );
             String username = el.attributeValue( "username" );
             String url = el.attributeValue( "url" );
             String pwd = el.attributeValue( "pwd" );
             ds.setUsername(username);
             ds.setUrl(url);
             ds.setPassword(pwd);
             ds.setInitialSize( Integer.valueOf(initialSize));
             ds.setMaxActive(Integer.valueOf(maxActive));
             ds.setMinIdle(Integer.valueOf(minIdle));
             ds.setMaxWait(Integer.valueOf(maxWait));
             ds.setTestOnBorrow(testOnBorrow.equals( "true" )? true : false );
             ds.setTestOnReturn(testOnReturn.equals( "true" )? true : false );
             ds.setTestWhileIdle(testWhileIdle.equals( "true" )? true : false );
            ds.setTimeBetweenEvictionRunsMillis(Long.valueOf(timeBetweenEvictionRunsMillis));
            ds.setMinEvictableIdleTimeMillis(Long.valueOf(minEvictableIdleTimeMillis));
             ds.setRemoveAbandoned(removeAbandoned.equals( "true" )? true : false );
             ds.setRemoveAbandonedTimeout(Integer.valueOf(removeAbandonedTimeout));
             ds.setLogAbandoned(logAbandoned.equals( "true" )? true : false );
             ds.setFilters(filters);
             acf.registerSingleton(id, ds);
             dsMap.put(DataSourceType.valueOf(id), ds);
         }
         this .setTargetDataSources(dsMap);
         setDefaultTargetDataSource(dsMap.get( "game_server_1" )); //设置默认数据源
     }
 
 
 
     @Override
     protected Object determineCurrentLookupKey() {
         DataSourceType dataSourceType= DataSourceContextHolder.getDataSourceType();
         
         return dataSourceType;
     }
 
     @Override
     public void setDataSourceLookup(DataSourceLookup dataSourceLookup) {
         super .setDataSourceLookup(dataSourceLookup);
     }
 
     @Override
     public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
         super .setDefaultTargetDataSource(defaultTargetDataSource);
     }
 
     @Override
     public void setTargetDataSources(Map targetDataSources) {
         super .setTargetDataSources(targetDataSources);
     }
 
}


xml


<bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">false
                </prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.Oracle9iDialect
                </prop>
                <prop key="hibernate.show_sql">true
                </prop>
                <prop key="hibernate.query.substitutions">true 1, false 0, yes ‘Y‘, no ‘N‘
                </prop>
                <prop key="hibernate.bytecode.use_reflection_optimizer">true
                </prop>
                <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider
                </prop>
                <prop key="hibernate.cache.use_query_cache">true
                </prop>
                <prop key="hibernate.cache.use_structured_entries">false
                </prop>
                <prop key="hibernate.max_fetch_depth">0
                </prop>
                <prop key="hibernate.jdbc.fetch_size">50
                </prop>
                <prop key="hibernate.jdbc.batch_size">20
                </prop>
            </props>
        </property>
         <property name="dataSource">
            <ref bean="mutiDataSourceBean" />
        </property> 
    </bean>
    <bean id="jdbcExceptionTranslator"
        class="org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator">
         <property name="dataSource">
            <ref bean="mutiDataSourceBean" />
        </property> 
    </bean>
    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory">
            <ref bean="sessionFactory" />
        </property>
        <property name="jdbcExceptionTranslator">
            <ref bean="jdbcExceptionTranslator" />
        </property>
    </bean>
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory">
            <ref local="sessionFactory" />
        </property>
    </bean>

 <bean id="mutiDataSourceBean" class="com.mshop.common.dataSource.MutiDataSourceBean">  
    </bean>


OK,大公告成. 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class TestMutiDataSource  extends SpringTxTestCase{
 
     @Autowired
     @Qualifier ( "jdbcTemplate4Game" )
     JdbcTemplate  jt;
     
     @Test
     public void test() {
 
         DataSourceContextHolder.setDataSourceType(DataSourceType.game_server_1);
         List<Map<String, Object>> list = jt.queryForList( "select* from fs_accountlog.t_accountlogin" );
         for (Map<String, Object> map : list) {
             System.out.println(map);
         }
     }
 
}

http://www.aiuxian.com/article/p-1449920.html

spring配置文件---之数据库切换

标签:

原文地址:http://blog.csdn.net/u012099568/article/details/51423837

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!