spring多数据源配置
来源:互联网 发布:苹果id解锁软件下载 编辑:程序博客网 时间:2024/05/21 10:50
主要内容转载自别人的博客,加了一些自己的备注和理解
http://blog.csdn.net/fyxxq/article/details/8684039
在Spring 2.0.1中引入了AbstractRoutingDataSource, 该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上。
Spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性。而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据。
Spring对于多数据源,以数据库表为参照,大体上可以分成两大类情况:
一是,表级上的跨数据库。即,对于不同的数据库却有相同的表(表名和表结构完全相同)。
二是,非表级上的跨数据库。即,多个数据源不存在相同的表。
Spring2.x的版本中采用Proxy模式,就是我们在方案中实现一个虚拟的数据源,并且用它来封装数据源选择逻辑,这样就可以有效地将数据源选择逻辑从Client中分离出来。Client提供选择所需的上下文(因为这是Client所知道的),由虚拟的DataSource根据Client提供的上下文来实现数据源的选择。
具体的实现就是,虚拟的DataSource仅需继承AbstractRoutingDataSource实现determineCurrentLookupKey()在其中封装数据源的选择逻辑。
一、原理
首先看下AbstractRoutingDataSource类结构,继承了AbstractDataSource
既然是AbstractDataSource,当然就是javax.sql.DataSource的子类,于是我们自然地回去看它的getConnection方法:
return determineTargetDataSource().getConnection();
}
public Connection getConnection(String username, String password) throws SQLException {
return determineTargetDataSource().getConnection(username, password);
}
* {@link #determineCurrentLookupKey() current lookup key}, performs
* a lookup in the {@link #setTargetDataSources targetDataSources} map,
* falls back to the specified
* {@link #setDefaultTargetDataSource default target DataSource} if necessary.
* @see #determineCurrentLookupKey()
*/
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
那么resolvedDataSources和resolvedDefaultDataSource又从哪里来呢,答案是在spring容器中注入bean的时候,
上文中看到该抽象类实现了InitializingBean接口,该接口用于bean注入时初始化参数,而AbstractRoutingDataSource已经实现了该接口的afterPropertiesSet方法,将配置文件中的targetDataSources和defaultTargetDataSource分别注入了resolvedDataSources和resolvedDefaultDataSource。
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);
}
}
targetDataSources和defaultTargetDataSource将在下文的配置文件中注入。
二、Spring配置多数据源的方式和具体使用过程
1. 建立一个获得和设置上下文环境的类DatasourceContextHolder,主要负责改变上下文数据源的名称
// 线程本地环境
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
// 设置数据源类型
public static void setCustomerType(String customerType) {
contextHolder.set(customerType);
}
// 获取数据源类型
public static String getCustomerType() {
return contextHolder.get();
}
// 清空数据源类型
public static void clearCustomerType() {
contextHolder.remove();
}
}
private Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
String datasource = DatasourceContextHolder.getCustomerType();
logger.info("Current datasource: [{}]", datasource);
return datasource;
}
}
3、编写spring的配置文件配置多个数据源
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClass"
value="oracle.jdbc.pool.OracleConnectionPoolDataSource" />
<property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl" />
<property name="user" value="isc_v10" />
<property name="password" value="isc" />
</bean>
<!-- 数据源 -->
<bean id="orclDataSource" parent="parentDataSource">
<property name="user" value="orcl" />
<property name="password" value="orcl" />
</bean>
<!-- 数据源 -->
<bean id="iscDataSource" parent="parentDataSource">
<property name="user" value="isc_v10" />
<property name="password" value="isc" />
</bean>
<!-- 编写spring 配置文件的配置多数源映射关系 -->
<bean id="dataSource" class="com.wy.config.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="orcl" value-ref="orclDataSource"></entry>
<entry key="isc" value-ref="iscDataSource"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="orclDataSource">
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
4.测试
// hibernate创建实体
DynamicDataSourceHolder.setDataSourceType("orcl");// 设置为另一个数据源
com.wy.domain.Test user = new com.wy.domain.Test();
user.setName("WY");
user.setAddress("BJ");
testDao.save(user);// 使用dao保存实体
DynamicDataSourceHolder.setDataSourceType("isc");// 设置为另一个数据源
testDao.save(user);// 使用dao保存实体到另一个库中
}
ps:实际开发过程中不可能手动的执行DynamicDataSourceHolder的setDataSourceType方法来指定数据源,太low了,通常的做法是通过拦截器或者aop的通知,下面以aop为例
<bean id="dataSourceInterceptor" class="com.luming.core.datasource.DatasourceInterceptor">
<property name="defaultDatasource" value="lumingdefaultDatasource"/>
<property name="datasourceMapping">
<map>
<!-- dao.* 会匹配dao package下的所有sub package下的所有类-->
<entry key="com.luming.orcl.dao.*" value="orcl"/>
<entry key="com.luming.isc.dao.*" value="isc"/>
</map>
</property>
</bean>
<!-- 数据源切换的切面,切点为所有dao中的所有类与方法-->
<aop:config>
<aop:aspect id="datasourceAspect" ref="dataSourceInterceptor">
<!-- dao.. 会匹配dao package下的所有sub package下的所有类-->
<aop:pointcut id="switchPoint" expression="execution(* com.luming.*.dao..*(..)))"/>
<aop:before method="switchDatasource" pointcut-ref="switchPoint"/>
</aop:aspect>
</aop:config>
private Map<String, String> datasourceMapping;
private String defaultDatasource;
private Map<String, String> datasources = new HashMap<>();
private Map<String, Pattern> patternMap = new HashMap<>();
public void setDatasourceMapping(Map<String, String> datasourceMapping) {
this.datasourceMapping = datasourceMapping;
}
public void setDefaultDatasource(String defaultDatasource) {
this.defaultDatasource = defaultDatasource;
}
//操作前执行,设置数据源
public void switchDatasource(JoinPoint jp) {
//不同的dao不同的包名
String packageName = jp.getSignature().getDeclaringType().getPackage().getName();
if(datasources.containsKey(packageName)){
DatasourceContextHolder.setCustomerType(datasources.get(packageName));
return;
}
for(String key : datasourceMapping.keySet()){
Pattern pattern = getPackagePattern(key);
//根据包名匹配key
if(pattern.matcher(packageName).find()){
String targetDatasource = datasourceMapping.get(key);
datasources.put(packageName, targetDatasource);
DatasourceContextHolder.setCustomerType(targetDatasource);
return;
}
}
if(StringUtils.isNotBlank(defaultDatasource)){
datasources.put(packageName, defaultDatasource);
DatasourceContextHolder.setCustomerType(defaultDatasource);
return;
}
throw XExceptionFactory.create("F_CORE_DS_1001");
}
private Pattern getPackagePattern(String patternStr){
if(patternMap.containsKey(patternStr)){
return patternMap.get(patternStr);
}
Pattern pattern = Pattern.compile(patternStr);
patternMap.put(patternStr, pattern);
return pattern;
}
}
- spring配置多数据源
- Spring多数据源配置
- Spring多数据源配置
- spring多数据源配置
- Spring配置多数据源
- Spring多数据源配置
- Spring配置多数据源
- spring多数据源配置
- spring多数据源配置
- Spring多数据源配置
- spring 多数据源配置
- spring 配置多数据源
- Spring多数据源配置
- spring多数据源配置
- Spring多数据源配置
- Spring多数据源配置
- spring多数据源配置
- spring配置多数据源
- Android权限判断checkPermission
- 求学生平均身高
- 智能指针仿真-001-基础篇
- 如何把一个UITableView滚动到tableFooterView?
- 给研究生的建议
- spring多数据源配置
- 获得xml解析器LayoutInflater 实例的三种方式
- button设置圆角代码----以及一些常用的属性
- 【ecmall】解决无法上传店铺logo和banner照片问题(一)
- checkbox全选反选/button切换显示隐藏
- Java 集合体系详解——Set体系无序不重复集合
- const char *,char * ,string,char []之间的关系和转换
- SqlLoad 使用
- C++ static const 修饰成员