Spring Data JPA 多数据源的使用

2023-11-16

    <p>项目中使用多个数据源在以往工作中比较常见,微服务架构中不建议一个项目使用多个数据源。在微服务架构下,一个微服务拥有自己独立的一个数据库,如果此微服务要使用其他数据库的数据,需要调用对应库的微服务接口来调用,而不是在一个项目中连接使用多个数据库,这样微服务更独立、更容易水平扩展。</p>

虽然在微服务架构下,不提倡一个项目拥有多个数据源,但在 Spring Boot 体系中,项目实现多数据源调用却是一件很容易的事情,本节课将介绍 Spring Data JPA 多数据源的使用。

Spring Data JPA 使用多数据源的整体思路是,配置不同的数据源,在启动时分别加载多个数据源配置,并且注入到不同的 repository 中。这样不同的 repository 包就有不同的数据源,使用时注入对应包下的 repository,就会使用对应数据源的操作。

对照前两课的示例项目,本课内容将会对项目结构有所调整,如下:
在这里插入图片描述
其中:

  • config 启动时加载、配置多数据源;
  • model 存放数据操作的实体类;
  • repository 目录下有两个包路径 test1 和 test2 ,分别代表两个不同数据源下的仓库,这两个包下的 repository 可以相同也可以不同。

下面演示一下项目。
配置 Spring Data JPA 对多数据源的使用,一般分为以下几步:

  • 创建数据库 test1 和 test2
  • 配置多数据源
  • 不同源的 repository 放入不同包路径
  • 声明不同的包路径下使用不同的数据源、事务支持
  • 不同的包路径下创建对应的 repository

测试使用

上面的一些步骤我们在前面两课中已经讲过了,这里只补充不同的内容。

配置两个数据源:

spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/test1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.primary.username=root
spring.datasource.primary.password=root
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.secondary.username=root
spring.datasource.secondary.password=root
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
#SQL 输出
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.hbm2ddl.auto=create
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
#format 一下 SQL 进行输出
spring.jpa.properties.hibernate.format_sql=true

设置将项目中的 SQL 格式化后打印出来,方便在开发过程中调试跟踪

创建 DataSourceConfig 添加 @Configuration 注解,在项目启动时运行初始化数据库资源。

@Configuration
public class DataSourceConfig {
}

在 DataSourceConfig 类中加载配置文件,利用 ConfigurationProperties 自动装配的特性加载两个数据源。

加载第一个数据源,数据源配置以 spring.datasource.primary 开头,注意当有多个数据源时,需要将其中一个标注为 @Primary,作为默认的数据源使用。

@Bean(name = "primaryDataSource")
@Primary
@ConfigurationProperties("spring.datasource.primary")
public DataSource firstDataSource() {
    return DataSourceBuilder.create().build();
}

加载第二个数据源,数据源配置以 spring.datasource.secondary 为开头。

@Bean(name = "secondaryDataSource")
@ConfigurationProperties("spring.datasource.secondary")
public DataSource secondDataSource() {
    return DataSourceBuilder.create().build();
}

加载 JPA 的相关配置信息,JpaProperties 是 JPA 的一些属性配置信息,构建 LocalEntityManagerFactoryBean 需要参数信息注入到方法中。

@Autowired
private JpaProperties jpaProperties;
@Autowired
private HibernateProperties hibernateProperties;

@Bean(name = “vendorProperties”)
public Map<String, Object> getVendorProperties() {
return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
}

第一个数据源的加载配置过程

首先来看第一个数据源的加载配置过程,创建 PrimaryConfig 类,将上面创建好的第一个数据源注入到类中,添加 @Configuration 和 @EnableTransactionManagement 注解,第一个代表启动时加载,第二个注解表示启用事务,同时将第一个数据源和 JPA 配置信息注入到类中。

@Configuration
@EnableTransactionManagement
public class PrimaryConfig {
    @Autowired
    @Qualifier("primaryDataSource")
    private DataSource primaryDataSource;
    @Autowired
    @Qualifier("vendorProperties")
    private Map<String, Object> vendorProperties;
}

LocalEntityManagerFactoryBean 负责创建一个适合于仅使用 JPA 进行数据访问的环境的 EntityManager,构建的时候需要指明提示实体类的包路径、数据源和 JPA 配置信息。

@Bean(name = "entityManagerFactoryPrimary")
@Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary (EntityManagerFactoryBuilder builder) {
    return builder
            .dataSource(primaryDataSource)
            .properties(vendorProperties)
            .packages("com.neo.model") //设置实体类所在位置
            .persistenceUnit("primaryPersistenceUnit")
            .build();
}

利用上面的 entityManagerFactoryPrimary() 方法构建好最终的 EntityManager。

@Bean(name = "entityManagerPrimary")
@Primary
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
    return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
}

EntityManager 是 JPA 中用于增、删、改、查的接口,它的作用相当于一座桥梁,连接内存中的 Java 对象和数据库的数据存储。使用 EntityManager 中的相关接口对数据库实体进行操作的时候, EntityManager 会跟踪实体对象的状态,并决定在特定时刻将对实体的操作映射到数据库操作上面。

同时给数据源添加上 JPA 事务。

@Bean(name = "transactionManagerPrimary")
@Primary
PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
    return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
}

最后一步最为关键,将我们在类中配置好的 EntityManager 和事务信息注入到对应数据源的 repository 目录下,这样此目录下的 repository 就会拥有对应数据源和事务的信息。

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef="entityManagerFactoryPrimary",
        transactionManagerRef="transactionManagerPrimary",
        basePackages= { "com.neo.repository.test1" })//设置dao(repo)所在位置
public class PrimaryConfig {}

其中,basePackages 支持设置多个包路径,例如,basePackages= { “com.neo.repository.test1”,“com.neo.repository.test3” }

到此第一个数据源配置完成了。

第二个数据源的加载配置过程

第二个数据源配置和第一个数据源配置类似,只是方法上去掉了注解:@Primary,第二个数据源数据源加载配置类 SecondaryConfig 完整代码如下:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef="entityManagerFactorySecondary",
        transactionManagerRef="transactionManagerSecondary",
        basePackages= { "com.neo.repository.test2" })
public class SecondaryConfig {
    @Autowired
    @Qualifier("secondaryDataSource")
    private DataSource secondaryDataSource;
@Autowired
@Qualifier("vendorProperties")
private Map&lt;String, Object&gt; vendorProperties;

@Bean(name = "entityManagerFactorySecondary")
public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary (EntityManagerFactoryBuilder builder) {
    return builder
            .dataSource(secondaryDataSource)
            .properties(vendorProperties)
            .packages("com.neo.model")
            .persistenceUnit("secondaryPersistenceUnit")
            .build();
}
@Bean(name = "entityManagerSecondary")
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
    return entityManagerFactorySecondary(builder).getObject().createEntityManager();
}
@Bean(name = "transactionManagerSecondary")
PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder) {
    return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
}
}

到此多数据源的配置就完成了,项目中使用哪个数据源的操作,就注入对应包下的 repository 进行操作即可,接下来我们对上面配置好的数据源进行测试。

创建 UserRepositoryTests 测试类,将两个包下的 repository 都注入到测试类中:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserRepositoryTests {
    @Resource
    private UserTest1Repository userTest1Repository;
    @Resource
    private UserTest2Repository userTest2Repository;
}

首先测试两个数据库中都存入数据,数据源1插入 2 条用户信息,数据源2插入 1 条用户信息。

@Test
public void testSave() throws Exception {
    Date date = new Date();
    DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
    String formattedDate = dateFormat.format(date);
    userTest1Repository.save(new User("aa", "aa123456","aa@126.com", "aa",  formattedDate));
    userTest1Repository.save(new User("bb", "bb123456","bb@126.com", "bb",  formattedDate));
    userTest2Repository.save(new User("cc", "cc123456","cc@126.com", "cc",  formattedDate));
}

执行完测试用例后查看数据库,发现 test1 库有两条数据,test2 有一条,证明两个数据源均保存数据正常。下面继续测试删除功能,使用两个数据源的 repository 将用户信息全部删除。

@Test
public void testDelete() throws Exception {
    userTest1Repository.deleteAll();
    userTest2Repository.deleteAll();
}

执行完测试用例后,发现 test1 库和 test2 库用户表的信息已经被清空,证明多数据源删除成功。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring Data JPA 多数据源的使用 的相关文章

随机推荐

  • 机器学习有监督学习之--回归

    一 引言 本材料参考Andrew Ng大神的机器学习课程 http cs229 stanford edu 以及斯坦福无监督学习UFLDL tutorial http ufldl stanford edu wiki index php UFL
  • Linux 多线程调试(内存占用、死循环、CPU占用率高……)

    你的软件在某个时刻停止服务 CPU占用达到100 这种问题一个可能的原因是产生了死循环 假设程序某处存在潜在的死循环 并在某种条件下会引发 本文以一个示例来定位出现死循环的位置 当程序某处存在死循环 通常定位问题及缩小范围的方法是 在可疑的
  • 数据库Sharding集群:扩容问题解决方案

    数据库Sharding集群扩容问题方案 MySQLSharding集群一般按照用户id进行哈希分区 这里面存在两个问题 1 集群的容量不够怎么办 2 单个用户的数据量太大怎么办 一 问题一 对于第1个问题 MySQLSharding集群往往
  • UART与TTL

    一 首先UART和TTL完全就是两码事 UART是通用异步收发传输器 Universal Asynchronous Receiver Transmitter 通常称作UART 是一种串行异步收发协议 TTL电平信号规定 5V等价于逻辑 1
  • DataOutputStream 类与BufferedOutputStream类的区别是什么

    DataOutputStream 类与Buffere dOutputStream类的区别是什么 DataOutputStream dataout new DataOutputStream new FileOutputStream file
  • Google Earth Engine(GEE) 01-中输入提示快捷键Ctrl+space无法使用的问题

    Google Earth Engine GEE 01 中输入提示快捷键Ctrl space无法使用的问题 GEE中 Ctrl space组合键用于代码输入快捷提示 能够提高编码的准确度和速度 但是 windows系统默认Ctrl space
  • Windows下Jenkins的运行环境由Java8 升级为Java11

    开源 Devops 工具 Jenkins 在官方博客宣布 从 6 月 28 日发布的 Jenkins 2 357 和将于 9 月发布的 LTS 版本开始 Jenkins 需要 Java 11 才能使用 将放弃 Java 8 步骤 1 安装j
  • STM32CUBEMX配置教程(一)基础配置

    STM32CUBEMX配置教程 一 基础配置 基于STM32H743VI 使用STM32CUBEMX两年了 始终觉得这个工具非常的方便 但因为不是经常使用 导致有些要点总是会有些遗忘 因此写下这一系列教程以供记忆 顺便让我这个大萌新给广大小
  • c++智能指针之auto_ptr详解(有源码有实例)

    前言 内存泄漏大概是每一个c c 程序员最深恶痛绝的问题 因为大部分此类问题都是令广大c程序员很抓狂 掉头发的疑难杂症 而内存泄漏的根本原因就是指针的使用不当引起的 例如指针指向的内存没有释放 导致产生了程序无法控制的内存块 而随着程序不断
  • Pycharm连接MySQL

    使用MySQL内置工具 命令 创建数据库 unicom 数据表 admin 表名 admin 列 id 整型 自增 主键 username 字符串 不为空 password 字符串 不为空 mobile 字符串 不为空 Python代码实现
  • VC++实现视频压缩编码标准 MPEG-4

    转载请标明是引用于 http blog csdn net chenyujing1234 欢迎大家提出意见 一起讨论 需要源码的请与我联系 参考书籍 lt
  • 测试人如何编写测试用例?一文从3个方面带你写一个合格的测试用例

    前言 作为一个测试新人 刚开始接触测试 对于怎么写测试用例很头疼 无法接触需求 只能根据站在用户的角度去做测试 但是这样情况会导致不能全方位的测试APP 这种情况就需要一份测试用例了 但是不会写 求指教 还有就是测试出来的bug该如何追踪
  • 访问数据库

    一 JDBC简介 JDBC时Java DataBase Connectivity的缩写 它是连接Java程序和数据库服务器的纽带 JDBC的实现封装了与各种数据库服务器通信的细节 Java程序通过JDBC API来访问数据库 有以下优点 1
  • 【leetcode刷题】最长回文子串

    题目描述 给你一个字符串 s 找到 s 中最长的回文子串 示例1 输入 s babad 输出 bab 解释 aba 同样是符合题意的答案 示例2 输入 s cbbd 输出 bb 方法一 暴力枚举 param string s return
  • 单片机概述习题以及答案

    一 填空 除了单片机这一名称之外 单片机还可称为 或 答 微控制器 嵌入式控制器 2 单片机与普通微型计算机的不同之处在于其将 和 三部分 通过内部 连接在一起 集成于一块芯片上 答 CPU 存储器 I O 口 总线 AT89S52 单片机
  • 线性代数期末抱佛脚

    1 Row operations steps for finding solution if possible in linear systems many linear equations 求解的方法有两种 第一种 如 若要求该矩阵的解
  • LSTM对比Bi-LSTM的电力负荷时间序列预测(Matlab)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 0 概述 1 电力负荷预测 2 滑动窗输入结构的构建 3 LSTM 4 Bi LSTM 5 运行结果 6 Matla
  • jQuery MiniUI 开发教程 树形控件 树形:懒加载树(五)

    b 懒加载树 b img http www miniui com docs api images lazytree gif img 参考示例 url http www miniui com demo tree lazytree html 懒
  • C++:读写INI文件

    C 读写INI文件 INI文件是一种常见的配置文件格式 用于存储应用程序的配置信息 在C 中 我们可以使用一些库来读取和写入INI文件 在本文中 我将向您展示如何使用C 读取和写入INI文件 读取INI文件 对于INI文件的读取 我们可以使
  • Spring Data JPA 多数据源的使用

    p 项目中使用多个数据源在以往工作中比较常见 微服务架构中不建议一个项目使用多个数据源 在微服务架构下 一个微服务拥有自己独立的一个数据库 如果此微服务要使用其他数据库的数据 需要调用对应库的微服务接口来调用 而不是在一个项目中连接使用多个