原文->http://exceptioneye.iteye.com/blog/1698064
在Spring 2.0.1中引入了AbstractRoutingDataSource, 该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上。
Spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性。而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据。
Spring对于多数据源,以数据库表为参照,大体上可以分成两大类情况:
一是,表级上的跨数据库。即,对于不同的数据库却有相同的表(表名和表结构完全相同)。
二是,非表级上的跨数据库。即,多个数据源不存在相同的表。
Spring2.x的版本中采用Proxy模式,就是我们在方案中实现一个虚拟的数据源,并且用它来封装数据源选择逻辑,这样就可以有效地将数据源选择逻辑从Client中分离出来。Client提供选择所需的上下文(因为这是Client所知道的),由虚拟的DataSource根据Client提供的上下文来实现数据源的选择。
具体的实现就是,虚拟的DataSource仅需继承AbstractRoutingDataSource实现determineCurrentLookupKey()在其中封装数据源的选择逻辑。
一、原理
首先看下AbstractRoutingDataSource类结构,继承了AbstractDataSource
- <span>public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean</span>
既然是AbstractDataSource,当然就是javax.sql.DataSource的子类,于是我们自然地回去看它的getConnection方法:
- <span>public Connection getConnection() throws SQLException {
- return determineTargetDataSource().getConnection();
- }
- public Connection getConnection(String username, String password) throws SQLException {
- return determineTargetDataSource().getConnection(username, password);
- }</span>
原来关键就在determineTargetDataSource()里:
- <span>/**
- * Retrieve the current target DataSource. Determines the
- * {@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;
- }</span>
这里用到了我们需要进行实现的抽象方法determineCurrentLookupKey(),该方法返回需要使用的DataSource的key值,然后根据这个key从resolvedDataSources这个map里取出对应的DataSource,如果找不到,则用默认的resolvedDefaultDataSource。
- <span>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);
- }
- }</span>
二、Spring配置多数据源的方式和具体使用过程
1、数据源的名称常量类
public enum DynamicDataSourceGlobal {
- ORCL,
- ISC
- }
2、建立一个获得和设置上下文环境的类,主要负责改变上下文数据源的名称
public class DynamicDataSourceHolder {
- // 线程本地环境
- private static final ThreadLocal<DynamicDataSourceGlobal> contextHolder = new ThreadLocal<DynamicDataSourceGlobal>();
- // 设置数据源类型
- public static void setDataSourceType(DynamicDataSourceGlobal dataSourceType) {
- Assert.notNull(dataSourceType, "DataSourceType cannot be null");
- contextHolder.set(dataSourceType);
- }
- // 获取数据源类型
- public static DynamicDataSourceGlobal getDataSourceType() {
- return (DynamicDataSourceGlobal) contextHolder.get();
- }
- // 清除数据源类型
- public static void clearDataSourceType() {
- contextHolder.remove();
- }
3、建立动态数据源类,注意,这个类必须继承AbstractRoutingDataSource,且实现方法 determineCurrentLookupKey,该方法返回一个Object,一般是返回字符串
public class DynamicDataSource extends AbstractRoutingDataSource {
- @Override
- protected Object determineCurrentLookupKey() {
- return DynamicDataSourceHolder.getDataSourceType();
- }
- }
4、编写spring的配置文件配置多个数据源
<!-- 数据源相同的内容 -->
- <bean id="parentDataSource"
- 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>
5、使用
@Test
- public void testSave() {
- // hibernate创建实体
- DynamicDataSourceHolder.setDataSourceType(DynamicDataSourceGlobal.ORCL);// 设置为另一个数据源
- com.wy.domain.Test user = new com.wy.domain.Test();
- user.setName("WY");
- user.setAddress("BJ");
- testDao.save(user);// 使用dao保存实体
- DynamicDataSourceHolder.setDataSourceType(DynamicDataSourceGlobal.ISC);// 设置为另一个数据源
- testDao.save(user);// 使用dao保存实体到另一个库中
- }
相关推荐
通过以上步骤,我们就实现了MyBatis与Spring配合下的动态数据源切换。这种机制有助于在多租户系统、读写分离或者高可用架构中灵活地管理数据库访问,提高了系统的可扩展性和灵活性。在实际应用中,还需要考虑数据源...
- **动态数据源切换逻辑**:核心逻辑是在AOP切面的Advice中实现。当一个需要切换数据源的方法被调用时,通过反射或其他机制获取该方法上标注的数据源信息,然后根据这些信息从数据源池中获取对应的数据源,并将其...
在企业级应用开发中,Spring框架的广泛使用使得...通过这种方式,我们可以灵活地处理复杂的数据源切换场景,适应不同业务的需求。在实际项目中,还需要考虑线程安全、异常处理、测试等方面的问题,确保系统稳定运行。
以下将详细介绍如何实现Spring的动态数据源切换以及相关知识点。 1. **数据源配置** - `jdbc.properties`: 这个文件通常包含了数据库连接的相关配置,如URL、用户名、密码、驱动类名等。Spring通过`Properties`类...
本文将深入探讨如何在SpringBoot项目中配置多数据源,并实现数据源的动态切换,帮助你理解和掌握这一核心技能。 首先,我们理解"多数据源"的概念。在SpringBoot应用中,多数据源意味着系统能够连接并操作多个不同的...
在 `thc-datasources` 压缩包中,可能包含了更详细的配置示例、数据源切换注解的实现以及相关的辅助工具类,如 `DynamicDataSourceContextHolder`。读者可以结合这些资源,进一步理解和实践多数据源动态切换的实现。
- AOP切面的Java类,实现了方法执行前后的拦截和数据源切换。 - 数据源相关的Java配置类,用于配置Spring的DataSource和MybatisPlus。 - Mapper接口和对应的XML文件,定义数据库操作。 - 业务逻辑层的Java类,其中的...
"Spring动态切换多数据源Demo"是一个示例项目,它展示了如何在Spring中实现灵活的数据源切换。 首先,我们需要了解Spring中的数据源。在Spring中,数据源(DataSource)是Java中定义的javax.sql.DataSource接口的...
4. **动态数据源切换**:对于MyBatis,可以在SqlSessionTemplate中设置数据源,或者使用`@MapperScan`注解配合自定义数据源路由类来实现。 **六、实战项目分析** 在`springboot-multi-datasource-master`项目中,...
- 可以编写单元测试或集成测试,模拟不同场景下的数据源切换,确保功能正确无误。 - 使用日志记录数据源切换的过程,以便于调试和监控。 8. **最佳实践** - 避免在业务代码中直接切换数据源,最好通过服务层的...
创建一个自定义的数据源切换注解,比如`@SwitchDataSource`,并在需要切换数据源的方法上使用。通过AspectJ的切面处理,我们可以在方法执行前后动态改变ThreadLocal中的数据源引用。 3. **Spring Cloud Config ...
1. **Spring的AbstractRoutingDataSource**:这是Spring提供的核心类,用于实现动态数据源切换。这个抽象类维护了一个数据源路由决策表,可以根据特定的规则(如事务上下文、线程局部变量等)来决定使用哪个数据源。...
在本文中,我们将深入探讨如何实现动态数据源切换,支持Spring声明式事务管理,并讨论多数据源回滚策略。以下是对这些知识点的详细说明: 1. **动态数据源切换**: - 通过AspectJ实现数据源的动态切换,可以设置在...
然后,定义一个数据源切换的抽象类或接口,用于封装切换逻辑: ```java public interface DataSourceSwitcher { DataSource switchDataSource(DataSourceType type); } ``` 接着,实现这个接口,基于Spring的`...
3. **AOP实现动态数据源切换** 使用AOP(面向切面编程)注解,我们可以实现方法级别的数据源切换。创建一个`@Aspect`注解的类,定义一个环绕通知来切换数据源: ```java @Aspect @Component public class ...
在Spring Boot应用中,数据源切换是一个常见的需求,特别是在分布式系统或者微服务架构中,可能需要连接多个数据库来实现不同的业务功能。本示例“spring boot数据源切换demo”将展示如何在Spring Boot中集成MyBatis...
在SpringBoot项目中,整合Mybatis-Plus并实现多数据源的动态切换,同时支持分页查询是一项常见的需求。以下将详细阐述这个过程中的关键步骤和技术要点。 首先,我们需要引入必要的Maven依赖。这里提到了四个关键...
总结起来,使用`ThreadLocal`和`AbstractRoutingDataSource`实现动态数据源切换的关键步骤包括: 1. 创建`ThreadLocal`实例来保存线程的数据源信息。 2. 实现`AbstractRoutingDataSource`,并重写`...
本文将深入探讨如何在Spring Boot项目中实现Atomikos分布式事务以及动态数据源切换的两种示例。 首先,我们需要理解什么是分布式事务。在分布式系统中,事务需要跨越多个独立的数据库或服务进行操作。分布式事务的...
本主题将深入探讨如何利用SpringBoot结合Atomikos实现动态多数据源以及事务管理,并介绍两种切换数据源的方法。 首先,SpringBoot简化了传统Spring应用的初始化过程,它通过自动配置和starter包让开发者快速搭建...