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

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

时间: 2024-10-02 06:22:16

spring配置文件---之数据库切换的相关文章

Spring代码中动态切换数据源

在Spring-Mybatis中,有这样一个类AbstractRoutingDataSource,根据名字可以猜到,这是一个框架提供的用于动态选择数据源的类.这个类有两个重要的参数,分别叫 defaultTargetDataSource和targetDataSources.一般的工程都是一个数据源,所以不太接触到这个类. [html] <bean id="myoneDataSource" class="org.apache.commons.dbcp2.BasicData

Spring配置文件元素&lt;context:property-placeholder location=&quot;classpath:application.properties&quot; /&gt;

<context:property-placeholder location="classpath*:*.properties" ignore-unresolvable="true"/> 1.有些参数在某些阶段中是常量 比如:a.在开发阶段我们连接数据库时的连接url,username,password,driverClass等 b.分布式应用中client端访问server端所用的server地址,port,service等 c.配置文件的位置 2.而

spring配置文件详解

转自: http://book.51cto.com/art/201004/193743.htm 此处详细的为我们讲解了spring2.5的实现原理,感觉非常有用 spring配置文件是用于指导Spring工厂进行Bean生产.依赖关系注入(装配)及Bean实例分发的"图纸".JavaEE程序员必须学会并灵活应用这份"图纸"准确地表达自己的"生产意图".Spring配置文件是一个或多个标准的XML文档,applicationContext.xml是

Hibernate SQL方言 (hibernate.dialect) Spring配置文件applicationContext.xml

转自:http://www.cnblogs.com/wj-wangjun/archive/2009/10/21/1587624.html Hibernate SQL方言 (hibernate.dialect) 数据库 hibernate方言 DB2 org.hibernate.dialect.DB2Dialect DB2 AS/400 org.hibernate.dialect.DB2400Dialect DB2 OS390 org.hibernate.dialect.DB2390Dialect

XML配置文件的命名空间与Spring配置文件中的头

一直以来,写Spring配置文件,都是把其他配置文件的头拷贝过来,最多改改版本号,也不清楚哪些是需要的,到底是干嘛的.今天整理一下,拒绝再无脑copy. 一.Spring配置文件常见的配置头 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http:/

使用Memcached、Spring AOP构建数据库前端缓存框架

数据库访问可能是很多网站的瓶颈.动不动就连接池耗尽.内存溢出等.前面已经讲到如果我们的网站是一个分布式的大型站点,那么使用 memcached实现数据库的前端缓存是个很不错的选择:但如果网站本身足够小只有一个服务器,甚至是vps的那种,不推荐使用memcached,使 用Hibernate或者Mybatis框架自带的缓存系统就行了. 一.开启memcached服务器端服务 如果已经安装了memcached服务器端程序,请确认服务器端服务已开启. 二.引入jar 1.  alisoft-xplat

J2EE进阶(四)Spring配置文件详解

J2EE进阶(四)Spring配置文件详解 前言 Spring配置文件是用于指导Spring工厂进行Bean生产.依赖关系注入(装配)及Bean实例分发的"图纸".Java EE程序员必须学会并灵活应用这份"图纸"准确地表达自己的"生产意图".Spring配置文件是一个或多个标准的XML文档,applicationContext.xml是Spring的默认配置文件,当容器启动时找不到指定的配置文档时,将会尝试加载这个默认的配置文件. 下面列举的是

Spring JDBC 访问数据库

Spring JDBC是Spring所提供的持久层技术,它以一种更直接.更简单的方式使用JDBC API.在Spring JDBC里,用户仅需要做那些必不可杀的事儿,而将资源获取.Statement创建.异常处理.资源释放等繁杂的工作交给Spring. 虽然ORM的框架已经很成熟,但是JDBC灵活直接的特性依旧让它有自己的用武之地. 本节的主要内容:使用JdbcTemplate模板类进行CRUD数据操作.BLOB和CLOB类型数据的操作,支持命名参数绑定NamedParameterJdbcTem

Spring 配置文件详解 (以2.5为例)

转载自:http://blog.csdn.net/zzjjiandan/article/details/22922847 Spring配置文件是用于指导Spring工厂进行Bean生产.依赖关系注入(装配)及Bean实例分发的"图纸".Java EE程序员必须学会并灵活应用这份"图纸"准确地表达自己的"生产意图".Spring配置文件是一个或多个标准的XML文档,applicationContext.xml是Spring的默认配置文件,当容器启动