1.业务需求
在项目开发中,经常会遇到需要操作多个数据源的情况,比如数据同步,在数据库A中增加数据的同时写入数据库B。这就需要做到可以配置多个数据源,并做到动态切换数据源。
2.多数据源配置
首先是多数据源的配置,在applicationContext.xml文件中。
主要配置代码如下:
<!-- 定义数据源的信息 -->
<bean id="parentDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="maxPoolSize">
<value>80</value>
</property>
<property name="minPoolSize">
<value>1</value>
</property>
<property name="initialPoolSize">
<value>1</value>
</property>
<property name="maxIdleTime">
<value>20</value>
</property>
</bean>
<bean id="dataSource_local" parent="parentDataSource">
<property name="jdbcUrl">
<value>jdbcUrl_local</value>
</property>
<property name="user">
<value>root</value>
</property>
<property name="password">
<value>password</value>
</property>
</bean>
<bean id="dataSource_remote" parent="parentDataSource">
<property name="jdbcUrl">
<value>jdbcUrl_remote</value>
</property>
<property name="user">
<value>root</value>
</property>
<property name="password">
<value>password</value>
</property>
</bean>
<!-- 动态DataSource配置 -->
<bean id="dynamicDataSource" class="gov.pbc.util.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="dataSource_local" value-ref="dataSource_local"/>
<entry key="dataSource_remote" value-ref="dataSource_remote"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSource_local"/>
</bean>
<!--定义Hibernate的SessionFactory -->
<!-- SessionFactory使用的数据源为上面的数据源 -->
<!-- 指定了Hibernate的映射文件和配置信息 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dynamicDataSource" />
<!-- 设置自动扫描,这样就不用一一注册model的ORM映射了 -->
<property name="packagesToScan">
<list>
<value>com.test.model</value>
</list>
</property>
<!-- 设置Hibernate的相关属性 -->
<property name="hibernateProperties">
<props>
<!-- 设置Hibernate的数据库方言 -->
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<!-- 设置Hibernate是否在控制台输出SOL语句,开发调试阶段通常设置为true -->
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
<prop key="hbm2ddl.auto">update</prop>
<prop key="hibernate.connection.release_mode">after_statement</prop>
</props>
</property>
</bean>
<bean id="hbnTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 开启基于@Transactional注解方式的事务管理 -->
<tx:annotation-driven transaction-manager="txManager"/>
3.数据源动态切换
首先,建立一个获得和设置上下文环境的类,主要负责改变上下文数据源的名称。代码如下:
public class CustomContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static final String DATA_SOURCE_LOCAL = "dataSource_local";//对应动态数据源配置中的key
public static final String DATA_SOURCE_REMOTE = "dataSource_remote";
public static void setCustomerType(String customerType) {
contextHolder.set(customerType);
}
public static String getCustomerType() {
return contextHolder.get();
}
public static void clearCustomerType() {
contextHolder.remove();
}
}
然后,建立动态数据源类,注意,这个类必须继承AbstractRoutingDataSource,且实现方法determineCurrentLookupKey。代码如下:
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
String sourceType = CustomContextHolder.getCustomerType();
//System.out.println("DataSourceType: "+sourceType);
return sourceType;
}
}
4.执行数据源的切换
在需要对某个数据库进行操作前,执行切换数据源的代码,个人习惯在service类中执行切换。示例代码如下:
import javax.annotation.Resource;
import com.test.dao.LoTeacherDAO;
import com.test.dao.ReTeacherDAO;
import com.test.model.LoTeacher;
import com.test.model.ReTeacher;
import com.test.util.CustomContextHolder;
public class TeacherServiceImpl implements TeacherService{
@Resource
private LoTeacherDAO loTeacherDAO;
@Resource
private ReTeacherDAO reTeacherDAO;
public void addLoTeacher(LoTeacher loTea){
CustomContextHolder.setCustomerType(CustomContextHolder.DATA_SOURCE_LOCAL);
loTeacherDAO.save(loTea);
}
public void addReTeacher(ReTeacher reTea){
CustomContextHolder.setCustomerType(CustomContextHolder.DATA_SOURCE_REMOTE);
reTeacherDAO.save(reTea);
}
}