在项目开发的过程中,有时我们有这样的需求,需要去调用别的系统中的数据,那么这个时候系统中就存在多个数据源了,那么我们如何来解决程序在运行的过程中到底是使用的那个数据源呢?
假设我们系统中存在2个数据源 mysql 和 oracle. 系统中存在 2个方法 methodA 和 methodB ,其中methodA是是需要去调用mysql、methodB是需要使用到oracle,那么在我们调用methodA或methodB时是如何知道使用的是mysql还是oracle呢? 我的做法是 使用Spring 的 动态数据源路由来解决这个问题。
需求: 系统中要显示一些图表信息,但是图表的数据是来自别的数据库,不是自己系统使用的数据库,如何来解决这个问题。
解决方案:在程序运行的过程中动态的去决定使用哪种数据源,借助Spring 的抽象数据源路由来解决。
前置条件:
1、@Primary 表示在程序中存在同一种类型的bean有多个时,默认使用有@Primay 注解标注的
2、当产生了Connection后,事务可能就会开启了
3、如果要获取到一个Connection,那么我们需要在事务开启前知道使用的是那个数据源,如果我们要写切面去判断使用那个数据源,那么一定要在事务切面之前
4、如果一个变量要在程序的任何地方都可以获取到,那么可以使用ThreadLocal来存放这个变量,如果存放在static类型的变量中,那么会存在线程安全问题。
解决思路:
1、在Spring 中提供了一个类 AbstractRoutingDataSource 这个类中有一个determineCurrentLookupKey() 方法,在这个方法中返回一个 key ,那么Spring 就知道使用那个数据源。那么Spring 怎么知道这个你返回的 key 是什么意思呢? 那么在这个类中必然有个方法 可以让 key 和 数据源 进行关联即 setTargetDataSources(Map<Object, Object> targetDataSources) 方法,在这个方法中的参数中传递一个map ,可以让map 的 key 为 determineCurrentLookupKey() 需要的key ,值为具体的数据源。
2、那么我们什么时候需要设置 determineCurrentLookupKey() 方法的key呢?那么肯定是那个方法需要切换数据源,即此时我们需要向ThreadLocal中设置当前数据源的key,那么假如我们有很多方法需要切换数据源,难道每个方法都要自己手动向ThreadLocal中插入值吗?这个代码太冗余了,那么我们此时就可以自定义一个注解,然后写一个切面凡是带这个注解的方法都进行拦截,然后再在这个切面中设置数据源需要的key.
3、那么我们的切面什么时候执行呢?我们知道事务是有Connection开启的,如果我们的切面在事务之后执行,那么什么意义也没有,即数据源切换失败。所以我们自己写的切面需要在Spring 的事务切面之前执行,使用@Order注解执行切面的执行顺序,注解里面的值越小越先执行。
4、我们知道我们的事务开启需要数据源,或Jpa操作数据库也需要数据源,那么我们应该将那个数据源作为主数据源呢?是我们系统的数据源还是第三方系统的数据源,这个肯定不是,应该有程序运行时动态决策,因此就需要一个 实现了 AbstractRoutingDataSource 的类作为主数据源。
具体步骤:
一、引入pom.xml文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.huan.springboot</groupId> <artifactId>springboot_20_multi_datasource</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springboot_20_multi_datasource</name> <url>http://maven.apache.org</url> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.3.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc14</artifactId> <version>10.2.0.4.0</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
二、配置2个数据源
1、配置application.yml文件,向里面增加2个不同数据源的配置
spring.datasource.mysql.url=jdbc:mysql://localhost/information_schema?useUnicode=true&characterEncoding=utf-8 spring.datasource.mysql.username=root spring.datasource.mysql.password=root spring.datasource.mysql.driver-class-name=com.mysql.jdbc.Driver spring.datasource.oracle.url=jdbc:oracle:thin:@127.0.0.1:1521:orcl spring.datasource.oracle.username=system spring.datasource.oracle.password=admin spring.datasource.oracle.driver-class-name=oracle.jdbc.driver.OracleDriver spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=none spring.jpa.hibernate.naming.strategy=org.hibernate.cfg.ImprovedNamingStrategy
2、编写 MultiDataSourceConfig 配置文件,配置这2个数据源
package com.huan.springboot.config; import javax.sql.DataSource; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 多数据源配置 * * @描述 * @作者 huan * @时间 2018年3月8日 - 下午8:47:30 */ @Configuration public class MultiDataSourceConfig { @Bean @ConfigurationProperties("spring.datasource.mysql") public DataSource mysqlDataSource() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("spring.datasource.oracle") public DataSource oracleDataSource() { return DataSourceBuilder.create().build(); } }
三、编写 自定义注解 + 数据源路由
1、自定义一个注解 DynamicDataSource
package com.huan.springboot.datasource; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 动态数据源注解 * * @描述 * @作者 huan * @时间 2018年3月8日 - 下午8:55:23 */ @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DynamicDataSource { /** * 默认是mysql数据源 * * @return */ DynamicDataSourceTypeEnum value() default DynamicDataSourceTypeEnum.MYSQL; }
2、编写一个数据源枚举类,用来表示有哪些数据源,防止手动书写会写错
package com.huan.springboot.datasource; import lombok.AllArgsConstructor; import lombok.Getter; /** * 动态数据源类型枚举 * * @描述 * @作者 huan * @时间 2018年3月8日 - 下午8:55:52 */ @AllArgsConstructor @Getter public enum DynamicDataSourceTypeEnum { ORACLE("oracle", "oracle数据源"), // MYSQL("mysql", "当前使用的是mysql数据源"); private String type; private String desc; }
3、程序在需要切换数据源的方法会将上面的这个枚举类型保存,那么保存到哪里呢,肯定是线程安全的ThreadLocal中,因此需要这个类DynamicDataSourceTypeHolder
package com.huan.springboot.datasource; /** * 保存当前线程的数据源类型 * * @描述 * @作者 huan * @时间 2018年3月8日 - 下午8:59:09 */ public class DynamicDataSourceTypeHolder { private static final ThreadLocal<DynamicDataSourceTypeEnum> DATA_SOURCE_TYPE = new ThreadLocal<>(); public static void setDataSourceType(DynamicDataSourceTypeEnum dataSourceTypeEnum) { DATA_SOURCE_TYPE.set(dataSourceTypeEnum); } public static DynamicDataSourceTypeEnum getDataSourceType() { return DATA_SOURCE_TYPE.get(); } public static void clear() { DATA_SOURCE_TYPE.set(null); } }
4、当我们在需要切换数据源的方法上编写了 @DynamicDataSource 注解后,就需要有一个类来处理这个注解,因此就有了下面这个类。
package com.huan.springboot.datasource; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /** * 动态数据源切面 * * @描述 * @作者 huan * @时间 2018年3月8日 - 下午9:15:49 */ @Component @Aspect @Order(0) public class DynamicDataSourceAspect { @Around("@annotation(dataSource)") public Object invoked(ProceedingJoinPoint pjp, DynamicDataSource dataSource) throws Throwable { DynamicDataSourceTypeHolder.setDataSourceType(dataSource.value()); try { return pjp.proceed(); } finally { DynamicDataSourceTypeHolder.clear(); } } }
注意:
1、看 @Order里面的值,这个值比较小,会保证这个切面在事务切面之前执行。可以删除这个注解然后看一下效果。
2、这个类中将当前数据源的 key 保存到了 ThreadLocal 类型的变量中
5、有了上面这些内容,那么我们肯定需要 需要编写 动态数据源路由,用于决定是返回那个数据源key ,决定使用的是那个数据源
package com.huan.springboot.datasource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 动态的数据源路由,由此类决定具体使用的是那个数据源 * * @描述 * @作者 huan * @时间 2018年3月8日 - 下午9:01:58 */ public class DynamicDataSourceRouter extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { DynamicDataSourceTypeEnum dataSourceType = DynamicDataSourceTypeHolder.getDataSourceType(); if (null == dataSourceType) { System.out.println("没有获取到数据源,使用默认的数据源."); return null; } else { System.out.println(dataSourceType.getDesc()); return dataSourceType.getType(); } }
注意:
determineCurrentLookupKey() 这个方法返回了一个key ,那么Spring就知道使用那个数据源。那么Spring是怎么知道的呢 ,看下方的 DynamicDataSourceRoteConfig 配置类,这个类我上方还没有写。
6、上方编写了动态数据源路由,但是数据源的key和具体的数据源还没有进行配置,因此由下方这个配置类来进行配置。
package com.huan.springboot.config; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Primary; import org.springframework.transaction.annotation.EnableTransactionManagement; import com.huan.springboot.datasource.DynamicDataSourceRouter; import com.huan.springboot.datasource.DynamicDataSourceTypeEnum; /** * 数据源路由配置 * * @描述 * @作者 huan * @时间 2018年3月8日 - 下午9:15:02 */ @Configuration @EnableTransactionManagement public class DynamicDataSourceRoteConfig { @Autowired @Lazy private DataSource mysqlDataSource; @Autowired @Lazy private DataSource oracleDataSource; @Bean @Primary public DataSource dynamicDataSourceRoute() { DynamicDataSourceRouter dataSourceRouter = new DynamicDataSourceRouter(); // 默认是mysql 数据源 dataSourceRouter.setDefaultTargetDataSource(mysqlDataSource); // 映射某个key 为具体的数据源 Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DynamicDataSourceTypeEnum.MYSQL.getType(), mysqlDataSource); targetDataSources.put(DynamicDataSourceTypeEnum.ORACLE.getType(), oracleDataSource); dataSourceRouter.setTargetDataSources(targetDataSources); return dataSourceRouter; } }
注意: 在dynamicDataSourceRoute()方法中
1、使用了@Primary注解修饰,那么当需要使用到数据源的时候会注入这个,而不会注入有上方MultiDataSourceConfig配置的2个数据源。
2、在这个方法中设置了一个默认的数据源,即当没有获取到数据源的key时应该使用那个数据源
3、在这个方法中 有一个 dataSourceRouter.setTargetDataSources(targetDataSources); 这句。在这个里面有一个属性 targetDataSources ,可以看到它的key 和具体的数据源进行关联了。
到此动态切换数据源的代码就写完了,那么下方写一个小例子测试一下。
四、示例
1、示例代码
2、查看结果
相关推荐
首先,我们需要在`pom.xml`或`build.gradle`文件中添加相应的Spring Boot数据源依赖,例如MySQL和Oracle: ```xml <groupId>org.springframework.boot <artifactId>spring-boot-starter-data-jpa <groupId>...
在Spring Boot中,我们可以通过创建不同的`DataSource` bean来配置多个数据源。每个数据源通常会有一个对应的`JdbcTemplate`或`JpaEntityManagerFactory`。首先,我们需要在`application.yml`或`application....
在Spring Boot应用中,使用`spring-data-jpa`来配置MySQL多数据源是一项常见的需求,尤其是在构建大型分布式系统时,为了实现数据隔离、负载均衡或读写分离等目的。本教程将详细介绍如何在Spring Boot项目中配置多个...
在本教程中,我们将深入探讨如何在Spring Boot项目中配置和使用多数据源以及JdbcTemplate。 首先,让我们了解什么是`JdbcTemplate`。它是Spring提供的一种模板类,用于执行SQL语句,通过回调机制将结果转换为Java...
在Spring Boot 2框架中,实现多数据源的配置是一项重要的任务,特别是在大型企业级应用中,可能需要连接到不同的数据库来满足不同业务的需求。在这个项目中,我们有两个主要的数据访问技术:Hibernate和MyBatis,...
4. **配置多数据源**: 在Spring Boot中,可以通过配置不同的`@Configuration`类来设置多个数据源。每个数据源可以有自己的`DataSourceProperties`,并通过`@Bean`注解创建对应的`DataSource`实例。 5. **切换数据源...
本文详细介绍了如何在Spring Boot中配置多数据源,包括创建基础工程、配置数据源、配置MyBatis Plus以及Mapper的扫描。这种方式不仅可以提高系统的灵活性和可扩展性,还能有效地解决跨系统的数据交互问题。希望这些...
本文将深入探讨如何在SpringBoot项目中配置多数据源,并实现数据源的动态切换,帮助你理解和掌握这一核心技能。 首先,我们理解"多数据源"的概念。在SpringBoot应用中,多数据源意味着系统能够连接并操作多个不同的...
在Spring Boot应用中,多数据源配置是一项重要的技术实践,特别是在大型系统中,可能需要连接到多个数据库以实现数据隔离、读写分离或是分布式事务管理。Spring Boot以其强大的自动化配置能力,使得设置多数据源变得...
在Spring Boot应用中,配置多数据源是一项常见的需求,尤其对于那些需要同时连接不同数据库(如MySQL、Oracle等)的应用来说。Spring Boot以其简洁的配置和自动配置特性,使得这项任务变得相对简单。本文将详细讲解...
本项目是基于Spring Boot和MyBatis实现的多数据源配置示例,适合在Spring Tool Suite (STS) 开发环境中运行。 首先,我们需要理解Spring Boot的自动配置特性。Spring Boot通过`@EnableAutoConfiguration`注解简化了...
在Spring Boot应用中,多数据源的配置与管理是一项重要的任务,特别是在大型系统中,可能需要连接到不同的数据库以满足不同业务的需求。本教程将详细讲解如何在Spring Boot项目中集成Druid连接池,并利用AOP注解实现...
在Spring Boot项目中,多数据源配置是一项关键的技术,它允许我们连接并操作多个数据库,这对于数据隔离、读写分离或者分布式系统来说是至关重要的。本篇将详细讲解如何在基于Maven构建的Spring Boot应用中实现多...
本示例“spring boot数据源切换demo”将展示如何在Spring Boot中集成MyBatis Plus进行多数据源的配置与切换。 首先,我们需要了解Spring Boot对数据源的支持。Spring Boot默认支持多种数据库,如MySQL、Oracle等,...
Spring Boot配置动态数据源访问多个数据库实现代码详解 通过Spring Boot配置动态数据源访问多个数据库可以实现数据库的动态增删和数量无限的支持,下面将详细介绍该实现代码的知识点。 数据源配置管理 在Spring ...
请参考提供的资源文件,如`Spring Boot中使用多数据库 - JDBC.url`、`spring-boot jpa 配置两个数据源 - CSDN博客.url`以及`spring boot(七):springboot+mybatis多数据源最简解决方案 - 纯洁的微笑 - 博客园.url`,...
其内部可能包括了数据源配置、路由策略、切换逻辑等组件,通过Spring Boot的自动配置能力,使得开发者可以方便地在项目中集成和使用。 使用这个启动器,开发者可以自定义数据源的配置,例如设置数据源的类型(如...
在Spring Boot应用中,MongoDB的多数据源配置是一项关键任务,特别是在大型分布式系统中,可能需要连接到多个数据库以实现数据隔离、读写分离或灾难恢复策略。本篇文章将详细解析如何在Spring Boot中配置MongoDB的多...
在Spring Boot中,AOP(面向切面编程)和多数据源的整合是常见的应用场景,尤其是在大型企业级项目中,为了实现数据的隔离或者优化数据库访问,常常需要配置多个数据源。本文将深入探讨如何使用Spring Boot的AOP注解...
你可以下载并运行这个项目,通过阅读和理解代码,来更直观地学习如何在Spring Boot中实现动态多数据源和JTA分布式事务。 总之,Spring Boot的多数据源和JTA分布式事务功能为企业级应用提供了强大的支撑,让开发者...