使用spring 实现真正多数据源的动态加载及动态切换
来源:互联网 发布:c语言实验报告答案 编辑:程序博客网 时间:2024/06/05 07:36
公司需要做一个分析统计系统,该系统需要连接N台服务器结点,进行数据的统计分析操作,
项目是以spring为基础框架搭建的.收集现在网上的所有关于多数据源配置的方式,并没有自己十分满意的,例如我有N个数据源,按照现网可以搜索到的配置方式,都是在spring配置文件中配置N个datasource,并通过实现AbstractRoutingDataSource抽象类的子类进行多数据源的管理.这种情况个人认为很不合理,一来维护起来困难,二来,数据源的基本信息基本都一致的情况下,会造成配置文件重复性的文字.(比如:初始化连接数,最小连接数,最大连接数,等等通用的信息.)
而配置AbstractRoutingDataSource的子类必须进行targetDataSources属性的初始化,这也决定了如上所说的情况,如果有N个数据源的情况,会让配置文件显得非常冗长,也容易侵染其他业务bean配置.原因请看代码:
AbstractRoutingDataSource.java
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
//查找当前用户上下文变量中设置的数据源.
@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
/**
*数据源线程上下文对象.
*
*/
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
/**
* 初始化动态数据源
* @author Administrator
*
*/
/**
* 初始化动态数据源
* @author Administrator
*
*/
@Component
(
"mutiGameDs"
)
public
class
MutiDataSourceBean
extends
AbstractRoutingDataSource
implements
ApplicationContextAware{
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,大公告成.
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);
}
}
}
- 使用spring 实现真正多数据源的动态加载及动态切换
- Spring实现多数据源动态切换
- spring框架中多数据源创建加载并且实现动态切换的配置实例代码
- spring框架中多数据源创建加载并且实现动态切换的配置实例代码
- spring框架中多数据源创建加载并且实现动态切换的配置实例代码
- 基于Spring Boot实现Mybatis的多数据源切换和动态数据源加载
- Spring多数据源的动态切换
- Spring多数据源的动态切换
- Spring多数据源配置 及 动态额切换
- spring多数据源动态切换及事务
- spring+myBatisi实现多数据源动态切换
- Spring动态切换多数据源解决方案
- Spring动态切换多数据源解决方案
- Spring > 动态切换多数据源
- Spring动态切换多数据源解决方案
- Spring 多数据源动态切换
- spring框架---动态切换多数据源
- Spring动态切换多数据源
- CVE-2014-0195漏洞分析
- 使用Android Animation注意的地方
- 重写actionbar的up返回按钮
- 大整数乘法
- pscp 的使用
- 使用spring 实现真正多数据源的动态加载及动态切换
- Java基础之理解Annotation
- Base64算法详解和实现【备忘】
- spring 整合JDBC
- WML 是什么
- ccache:利用缓存加速编译过程
- struct cdev结构体以及相关函数
- java保留两位小数4种方法
- 最长公共前缀(LCP)