草庐IT

【Spring Cloud】多数据源配置

好好生活_ 2023-08-15 原文

引言

从8月份到现在,团队一直有一项资源整合迁移的任务。简单来说,原来的产品设计规划不合理,各业务组各做各的,导致各类核心数据分散于各个业务组。8月初资源整合方案落地并开始实施,我们组承担起了管控各类数据的任务,未来各业务组生产出的数据以及各业务组需要的数据都只有我们这一个入口。

由于涉及到不同类的数据,并且这些数据是分散在各个业务组不同项目的数据库中,而且迁移产生的逻辑代码也是临时性,写在不同的项目或写在某一项目中也不合理,所以我想到的是用最初的项目框架搭建一个新的项目,使用多数据源,直接访问各个项目的数据库获取源数据,也不需要其他项目组做什么配合工作,数据交接工作只需要给我们梳理清除各个表的关系即可,后续迁移完新项目也直接停止服务即可。

所以接下来写一篇总结,Spring Cloud框架中如何配置多数据源。其实和Spring Boot项目配置一样,只是Spring Cloud框架数据源切换的切面定义有所不同。

快速开始

1. Maven依赖

<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>3.2.0</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.1.10.RELEASE</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.13</version>
</dependency>

以上依赖主要是数据库相关的,Spring Cloud相关的依赖就不在这展示了,涉及的依赖包括像nacos注册中心、feign、ribbon等等。

2. 数据库类型定义

首先,把需要访问的所有数据库用一个枚举来定义,下面就用英文1-4表示4个不同的数据库。

其次,提供设置与访问数据源类型的方案,为切面设置不同的数据源使用。

public class DataSourceType {

   /**
	* 数据库类型
	*/
	public enum DataBaseType {
	   /**
	    * 数据库1
	    */
	   one,
	   /**
	    * 数据库2
	    */
	   two,
	   /**
	    * 数据库3
	    */
	   three,
	   /**
	    * 数据库4
	    */
	   four
	}

    /**
     * 使用ThreadLocal保证线程安全
     */
    private static final ThreadLocal<DataBaseType> TYPE = new ThreadLocal<>();

    /**
     * 往当前线程里设置数据源类型
     *
     * @param dataBaseType 数据源类型
     */
    public static void setDataBaseType(DataBaseType dataBaseType) {
        if (dataBaseType == null) {
            throw new NullPointerException();
        }
        TYPE.set(dataBaseType);
    }

    /**
     * 获取数据源类型
     *
     * @return 返回数据源的类型
     */
    public static DataBaseType getDataBaseType() {
        //默认使用数据库1
        return TYPE.get() == null ? DataBaseType.one : TYPE.get();
    }

    /**
     * 清空数据类型
     */
    public static void clearDataBaseType() {
        TYPE.remove();
    }
}

3. 数据库配置

在配置文件中添加各个数据库的连接地址、用户名和密码等信息。

spring:
  datasource:
    one:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: ****
      username: ****
      password: ****
      queryTimeout: 10000
    two:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: ****
      username: ****
      password: ****
      queryTimeout: 10000
    three:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: ****
      username: ****
      password: ****
      queryTimeout: 10000
    four:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: ****
      username: ****
      password: ****
      queryTimeout: 10000

4. 多数据源核心配置类

@Configuration
//若扫描不到mapper,添加以下注解配置基础包即可
@MapperScan(basePackages = "XXX", sqlSessionFactoryRef = "SqlSessionFactory")
public class DataSourceConfig {

    @Primary
    @Bean(name = "oneDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.one")
    public DataSource getOneDateSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "twoDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.two")
    public DataSource getTwoDateSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "threeDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.three")
    public DataSource getThreeDateSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "fourDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.four")
    public DataSource getFourDateSource() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 装载多个数据库源
     *
     * @param oneDataSource
     * @param twoDataSource
     * @param threeDataSource
     * @param fourDataSource
     * @return 返回数据源集合
     */
    @Bean(name = "dynamicDataSource")
    public DynamicDataSource dataSource(@Qualifier("oneDataSource") DataSource oneDataSource,
                                        @Qualifier("twoDataSource") DataSource twoDataSource,
                                        @Qualifier("threeDataSource") DataSource threeDataSource,
                                        @Qualifier("fourDataSource") DataSource fourDataSource) {
        Map<Object, Object> targetDataSource = new HashMap<>(16);
        targetDataSource.put(DataSourceType.DataBaseType.one, oneDataSource);
        targetDataSource.put(DataSourceType.DataBaseType.two, twoDataSource);
        targetDataSource.put(DataSourceType.DataBaseType.three, threeDataSource);
        targetDataSource.put(DataSourceType.DataBaseType.four, fourDataSource);
        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setTargetDataSources(targetDataSource);
        //默认数据源
        dataSource.setDefaultTargetDataSource(oneDataSource);
        return dataSource;
    }

    /**
     * 装配数据源添加扫描mapper.xml文件的路径位置
     *
     * @param dynamicDataSource 多数据库源对象
     * @return 返回sql会话工厂
     */
    @Bean(name = "SqlSessionFactory")
    public SqlSessionFactory setSqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) throws Exception {
        // 导入mybatis sql session配置
        MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
        // 指明数据源
        sessionFactory.setDataSource(dynamicDataSource);
        // 指明mapper.xml位置(配置文件中指明的xml位置会失效用此方式代替,具体原因未知)
//        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/**/*Mapper.xml"));
        // 指明实体扫描(多个package用逗号或者分号分隔)
        sessionFactory.setTypeAliasesPackage("XXX");
        // 导入mybatis配置
        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setJdbcTypeForNull(JdbcType.NULL);
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setCacheEnabled(false);
        // 配置打印sql语句
        configuration.setLogImpl(StdOutImpl.class);
        // 添加分页功能
        configuration.addInterceptor(new PaginationInterceptor());
        sessionFactory.setConfiguration(configuration);
        return sessionFactory.getObject();
    }

    /**
     * 注入事务管理
     */
    @Bean
    public DataSourceTransactionManager transactionManager(DynamicDataSource dynamicDataSource) {
        return new DataSourceTransactionManager(dynamicDataSource);
    }
}

5. 数据源切面

核心思想就是AOP,通过访问不同路径的包,做数据源切换。在Spring Cloud框架中,主要是通过不同的feign包来区分使用哪个数据源。

@Aspect
@Component
public class DataSourceAop {

    /**
     * 设定切面地址在所有调用以下包下的任意类的任意方法或接口时切换数据源成指定数据源
     */
    @Before("execution(* com.XXX.feign.impl.one..*(..))")
    public void setDataSourceOne() {
        DataSourceType.setDataBaseType(DataSourceType.DataBaseType.one);
    }

    @Before("execution(* com.XXX.feign.impl.two..*(..))")
    public void setDataSourceTwo() {
        DataSourceType.setDataBaseType(DataSourceType.DataBaseType.two);
    }

    @Before("execution(* com.XXX.feign.impl.three..*(..))")
    public void setDataSourceThree() {
        DataSourceType.setDataBaseType(DataSourceType.DataBaseType.three);
    }

    @Before("execution(* com.XXX.feign.impl.four..*(..))")
    public void setDataSourceFour() {
        DataSourceType.setDataBaseType(DataSourceType.DataBaseType.four);
    }
}

以上就是Spring Cloud框架的步骤及核心代码。

总结

从结果来看,当初用一个独立的项目配置多数据源还是非常正确的选择。因为各项目难免有不同的需求仍然在不断开发迭代中,迁移这项任务涉及多个项目本来就很复杂,所以这个多数据源项目还是起到了很大的帮助作用的。

有关【Spring Cloud】多数据源配置的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby-on-rails - 独立 ruby​​ 脚本的配置文件 - 2

    我有一个在Linux服务器上运行的ruby​​脚本。它不使用rails或任何东西。它基本上是一个命令行ruby​​脚本,可以像这样传递参数:./ruby_script.rbarg1arg2如何将参数抽象到配置文件(例如yaml文件或其他文件)中?您能否举例说明如何做到这一点?提前谢谢你。 最佳答案 首先,您可以运行一个写入YAML配置文件的独立脚本:require"yaml"File.write("path_to_yaml_file",[arg1,arg2].to_yaml)然后,在您的应用中阅读它:require"yaml"arg

  3. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  4. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  5. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  6. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  7. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  8. Vscode+Cmake配置并运行opencv环境(Windows和Ubuntu大同小异) - 2

    之前在培训新生的时候,windows环境下配置opencv环境一直教的都是网上主流的vsstudio配置属性表,但是这个似乎对新生来说难度略高(虽然个人觉得完全是他们自己的问题),加之暑假之后对cmake实在是爱不释手,且这样配置确实十分简单(其实都不需要配置),故斗胆妄言vscode下配置CV之法。其实极为简单,图比较多所以很长。如果你看此文还配不好,你应该思考一下是不是自己的问题。闲话少说,直接开始。0.CMkae简介有的人到大二了都不知道cmake是什么,我不说是谁。CMake是一个开源免费并且跨平台的构建工具,可以用简单的语句来描述所有平台的编译过程。它能够根据当前所在平台输出对应的m

  9. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  10. ruby-on-rails - 创建 ruby​​ 数据库时惰性符号绑定(bind)失败 - 2

    我正在尝试在Rails上安装ruby​​,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf

随机推荐