前言:对两个类的讲解
基于spring来配置和使用多数据源还是比较简单的,因为spring框架已经预留了这样的接口可以方便数据源的切换。
先看一下spring获取数据源的源码:
可以看到AbstractRoutingDataSource获取数据源之前会先调用determineCurrentLookupKey方法查找当前的lookupKey,这个lookupKey就是数据源标识。
因此通过重写这个查找数据源标识的方法就可以让spring切换到指定的数据源了。
第一步:创建一个DynamicDataSource的类,继承AbstractRoutingDataSource并重写determineCurrentLookupKey方法,代码如下:
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// 从自定义的位置获取数据源标识
return DynamicDataSourceHolder.getDataSource();
}
}
第二步:创建DynamicDataSourceHolder用于持有当前线程中使用的数据源标识,代码如下:
public class DynamicDataSourceHolder {
/**
* 注意:数据源标识保存在线程变量中,避免多线程操作数据源时互相干扰
*/
private static final ThreadLocal<String> THREAD_DATA_SOURCE = new ThreadLocal<String>();
public static String getDataSource() {
return THREAD_DATA_SOURCE.get();
}
public static void setDataSource(String dataSource) {
THREAD_DATA_SOURCE.set(dataSource);
}
public static void clearDataSource() {
THREAD_DATA_SOURCE.remove();
}
}
重点内容end
Spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性。而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据。
Spring配置多数据源的方式和具体使用过程。
**Spring对于多数据源,以数据库表为参照,大体上可以分成两大类情况:
一是,表级上的跨数据库。即,对于不同的数据库却有相同的表(表名和表结构完全相同)。
二是,非表级上的跨数据库。即,多个数据源不存在相同的表。**
Spring2.x的版本中采用Proxy模式,就是我们在方案中实现一个虚拟的数据源,并且用它来封装数据源选择逻辑,这样就可以有效地将数据源选择逻辑从Client中分离出来。Client提供选择所需的上下文(因为这是Client所知道的),由虚拟的DataSource根据Client提供的上下文来实现数据源的选择。
具体的实现就是,虚拟的DataSource仅需继承AbstractRoutingDataSource实现determineCurrentLookupKey()在其中封装数据源的选择逻辑。
步骤如下:
一、动态配置多数据源
1. 数据源的名称常量类:
package com.frogking.datasource;
public class DataSourceConst {
public static final String Admin="12";
public static final String User = "123";
}
- 建立一个获得和设置上下文环境的类,主要负责改变上下文数据源的名称:
package com.frogking.datasource;
package com.frogking.datasource;
public class DataSourceContextHolder {
private static final ThreadLocal contextHolder = new ThreadLocal(); // 线程本地环境
// 设置数据源类型
public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType);
}
// 获取数据源类型
public static String getDataSourceType() {
return (String) contextHolder.get();
}
// 清除数据源类型
public static void clearDataSourceType () {
contextHolder.remove();
}
}
- 建立动态数据源类,注意,这个类必须继承AbstractRoutingDataSource,且实现方法 determineCurrentLookupKey,该方法返回一个Object,一般是返回字符串:
package com.frogking.datasource;
package com.frogking.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
- 编写spring的配置文件配置多个数据源
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!-- 数据源相同的内容 -->
<bean
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
id="parentDataSource">
<property name="driverClassName"
value="com.mysql.jdbc.Driver">
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>root</value>
</property>
</bean>
<!-- 数据库test -->
<bean parent="parentDataSource" id="adminDataSource">
<property name="url">
<value>jdbc:mysql://localhost:3306/test</value>
</property>
</bean>
<!-- 数据库test1 -->
<bean parent="parentDataSource" id="userDataSource">
<property name="url">
<value>jdbc:mysql://localhost:3306/test2</value>
</property>
</bean>
<!-- 编写spring 配置文件的配置多数源映射关系 -->
<bean class="com.frogking.datasource.DynamicDataSource"
id="dataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry value-ref="adminDataSource" key="12"></entry>
<entry value-ref="userDataSource" key="123"></entry>
</map>
</property>
<property name="defaultTargetDataSource"
ref="adminDataSource">
</property>
</bean>
<!-- sessionFactory的配置 -->
<bean
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
id="sessionFactory">
<property name="dataSource">
<ref local="dataSource"></ref>
</property>
<!-- 实体映射资源 -->
<property name="mappingResources">
<list>
<value>com/frogking/entity/User.hbm.xml</value>
<value>com/frogking/entity/Admin.hbm.xml</value>
</list>
</property>
<!-- 为sessionFactory配置Hibernate属性 -->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sq">true</prop>
<prop key="hibernate.connection.autocommit">false</prop>
<prop key="hibernate.cache.use_query_cache">false</prop>
<prop key="hibernate.max_fetch_depth">2</prop>
<prop
key="hibernate.bytecode.use_reflection_optimizer">
true
</prop>
</props>
</property>
</bean>
<bean id="userDao" class="com.frogking.dao.impl.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
</beans>
5.删除Hibernate中表的配置文件catalog,他会自动匹配数据库如果指定catalog匹配将不启用
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="com.frogking.entity.Admin" table="admin" catalog="test">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="native" />
</id>
<property name="name" type="java.lang.String">
<column name="name" length="100" />
</property>
<property name="passwrod" type="java.lang.String">
<column name="passwrod" length="100" />
</property>
</class>
</hibernate-mapping>
六。写dao层
七。编写测试类测试是否成功
package com.frogking.test;
import static org.junit.Assert.fail;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.frogking.dao.IUserDao;
import com.frogking.datasource.DataSourceConst;
import com.frogking.datasource.DataSourceContextHolder;
import com.frogking.entity.User;
public class UserTest {
ApplicationContext ac= new ClassPathXmlApplicationContext("applicationContext.xml");
IUserDao userDao =(IUserDao)ac.getBean("userDao");
@Test
public void testSave() {
//hibernate创建实体
DataSourceContextHolder.setDataSourceType(DataSourceConst.Admin);//设置为另一个数据源
User user=new User();
user.setName("wzhhh");
user.setPassword("hhh");
userDao.save(user);//使用dao保存实体
DataSourceContextHolder.setDataSourceType(DataSourceConst.User);//设置为另一个数据源
userDao.save(user);//使用dao保存实体到另一个库中
}
}