百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

Spring JPA – Multiple Databases

liuian 2025-06-28 15:17 43 浏览

1. 概述

在本教程中,我们将为具有多个数据库的 Spring 数据 JPA 系统实现一个简单的 Spring 配置。

2. 实体

首先,让我们创建两个简单的实体,每个实体都位于一个单独的数据库中。

下面是第一个用户实体:

package com.baeldung.multipledb.model.user;

@Entity
@Table(schema = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    private String name;

    @Column(unique = true, nullable = false)
    private String email;

    private int age;
}

这是第二个实体,产品

package com.baeldung.multipledb.model.product;

@Entity
@Table(schema = "products")
public class Product {

    @Id
    private int id;

    private String name;

    private double price;
}

我们可以看到,这两个实体也被放置在独立的包中。当我们进入配置时,这将很重要。

3. JPA 存储库

接下来,让我们看一下我们的两个 JPA 存储库,UserRepository

package com.baeldung.multipledb.dao.user;

public interface UserRepository
  extends JpaRepository<User, Integer> { }

产品存储库

package com.baeldung.multipledb.dao.product;

public interface ProductRepository
  extends JpaRepository<Product, Integer> { }

再次注意我们如何在不同的包中创建这两个存储库。

4. 使用 Java 配置 JPA

现在我们将进入实际的 Spring 配置。我们将首先设置两个配置类 — 一个用于用户,另一个用于产品

在每个配置类中,我们需要为 User 定义以下接口:

  • 数据源
  • EntityManagerFactory (userEntityManager )
  • TransactionManager (userTransactionManager)

让我们先看一下用户配置:

@Configuration
@PropertySource({ "classpath:persistence-multiple-db.properties" })
@EnableJpaRepositories(
    basePackages = "com.baeldung.multipledb.dao.user", 
    entityManagerFactoryRef = "userEntityManager", 
    transactionManagerRef = "userTransactionManager"
)
public class PersistenceUserConfiguration {
    @Autowired
    private Environment env;
    
    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean userEntityManager() {
        LocalContainerEntityManagerFactoryBean em
          = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(userDataSource());
        em.setPackagesToScan(
          new String[] { "com.baeldung.multipledb.model.user" });

        HibernateJpaVendorAdapter vendorAdapter
          = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto",
          env.getProperty("hibernate.hbm2ddl.auto"));
        properties.put("hibernate.dialect",
          env.getProperty("hibernate.dialect"));
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Primary
    @Bean
    public DataSource userDataSource() {
 
        DriverManagerDataSource dataSource
          = new DriverManagerDataSource();
        dataSource.setDriverClassName(
          env.getProperty("jdbc.driverClassName"));
        dataSource.setUrl(env.getProperty("user.jdbc.url"));
        dataSource.setUsername(env.getProperty("jdbc.user"));
        dataSource.setPassword(env.getProperty("jdbc.pass"));

        return dataSource;
    }

    @Primary
    @Bean
    public PlatformTransactionManager userTransactionManager() {
 
        JpaTransactionManager transactionManager
          = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(
          userEntityManager().getObject());
        return transactionManager;
    }
}

请注意我们如何通过使用 @Primary 注释 bean 定义来使用 userTransactionManager 作为我们的主事务管理器。每当我们要隐式或显式注入事务管理器而不按名称指定哪个时,这很有帮助。

接下来,让我们讨论


PersistenceProductConfiguration
,其中我们定义了类似的 bean:

@Configuration
@PropertySource({ "classpath:persistence-multiple-db.properties" })
@EnableJpaRepositories(
    basePackages = "com.baeldung.multipledb.dao.product", 
    entityManagerFactoryRef = "productEntityManager", 
    transactionManagerRef = "productTransactionManager"
)
public class PersistenceProductConfiguration {
    @Autowired
    private Environment env;

    @Bean
    public LocalContainerEntityManagerFactoryBean productEntityManager() {
        LocalContainerEntityManagerFactoryBean em
          = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(productDataSource());
        em.setPackagesToScan(
          new String[] { "com.baeldung.multipledb.model.product" });

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto",
          env.getProperty("hibernate.hbm2ddl.auto"));
        properties.put("hibernate.dialect",
          env.getProperty("hibernate.dialect"));
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Bean
    public DataSource productDataSource() {
 
        DriverManagerDataSource dataSource
          = new DriverManagerDataSource();
        dataSource.setDriverClassName(
          env.getProperty("jdbc.driverClassName"));
        dataSource.setUrl(env.getProperty("product.jdbc.url"));
        dataSource.setUsername(env.getProperty("jdbc.user"));
        dataSource.setPassword(env.getProperty("jdbc.pass"));

        return dataSource;
    }

    @Bean
    public PlatformTransactionManager productTransactionManager() {
 
        JpaTransactionManager transactionManager
          = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(
          productEntityManager().getObject());
        return transactionManager;
    }
}

5. 简单测试

最后,让我们测试一下我们的配置。

为此,我们将创建每个实体的实例并确保已创建它:

@RunWith(SpringRunner.class)
@SpringBootTest
@EnableTransactionManagement
public class JpaMultipleDBIntegrationTest {
 
    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ProductRepository productRepository;

    @Test
    @Transactional("userTransactionManager")
    public void whenCreatingUser_thenCreated() {
        User user = new User();
        user.setName("John");
        user.setEmail("john@test.com");
        user.setAge(20);
        user = userRepository.save(user);

        assertNotNull(userRepository.findOne(user.getId()));
    }

    @Test
    @Transactional("userTransactionManager")
    public void whenCreatingUsersWithSameEmail_thenRollback() {
        User user1 = new User();
        user1.setName("John");
        user1.setEmail("john@test.com");
        user1.setAge(20);
        user1 = userRepository.save(user1);
        assertNotNull(userRepository.findOne(user1.getId()));

        User user2 = new User();
        user2.setName("Tom");
        user2.setEmail("john@test.com");
        user2.setAge(10);
        try {
            user2 = userRepository.save(user2);
        } catch (DataIntegrityViolationException e) {
        }

        assertNull(userRepository.findOne(user2.getId()));
    }

    @Test
    @Transactional("productTransactionManager")
    public void whenCreatingProduct_thenCreated() {
        Product product = new Product();
        product.setName("Book");
        product.setId(2);
        product.setPrice(20);
        product = productRepository.save(product);

        assertNotNull(productRepository.findOne(product.getId()));
    }
}

6. Spring 启动中的多个数据库

Spring Boot 可以简化上述配置。

默认情况下,Spring Boot 将使用 spring.datasource.* 前缀的配置属性实例化其默认数据源

spring.datasource.jdbcUrl = [url]
spring.datasource.username = [username]
spring.datasource.password = [password]

我们现在希望继续使用相同的方法来配置第二个数据源,但使用不同的属性命名空间

spring.second-datasource.jdbcUrl = [url]
spring.second-datasource.username = [username]
spring.second-datasource.password = [password]

因为我们希望 Spring Boot 自动配置来获取这些不同的属性(并实例化两个不同的数据源),所以我们将定义两个类似于前面部分的配置类:

@Configuration
@PropertySource({"classpath:persistence-multiple-db-boot.properties"})
@EnableJpaRepositories(
  basePackages = "com.baeldung.multipledb.dao.user",
  entityManagerFactoryRef = "userEntityManager",
  transactionManagerRef = "userTransactionManager")
public class PersistenceUserAutoConfiguration {
    
    @Primary
    @Bean
    @ConfigurationProperties(prefix="spring.datasource")
    public DataSource userDataSource() {
        return DataSourceBuilder.create().build();
    }
    // userEntityManager bean 

    // userTransactionManager bean
}
@Configuration
@PropertySource({"classpath:persistence-multiple-db-boot.properties"})
@EnableJpaRepositories(
  basePackages = "com.baeldung.multipledb.dao.product", 
  entityManagerFactoryRef = "productEntityManager", 
  transactionManagerRef = "productTransactionManager")
public class PersistenceProductAutoConfiguration {
   
    @Bean
    @ConfigurationProperties(prefix="spring.second-datasource")
    public DataSource productDataSource() {
        return DataSourceBuilder.create().build();
    }
   
    // productEntityManager bean 

    // productTransactionManager bean
}

现在,我们已经根据 Boot 自动配置约定在


persistence-multiple-db-boot.properties
中定义了数据源属性。

有趣的部分是用@ConfigurationProperties注释数据源 Bean 创建方法。我们只需要指定相应的配置前缀在这种方法中,我们使用的是DataSourceBuilder,Spring Boot将自动处理其余的工作。

但是,如何将配置的属性注入到数据源配置中呢?

DataSourceBuilder 上调用 build() 方法时,它将调用其私有 bind() 方法:

public T build() {
    Class<? extends DataSource> type = getType();
    DataSource result = BeanUtils.instantiateClass(type);
    maybeGetDriverClassName();
    bind(result);
    return (T) result;
}

此私有方法执行大部分自动配置魔术,将解析的配置绑定到实际的数据源实例:

private void bind(DataSource result) {
    ConfigurationPropertySource source = new MapConfigurationPropertySource(this.properties);
    ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases();
    aliases.addAliases("url", "jdbc-url");
    aliases.addAliases("username", "user");
    Binder binder = new Binder(source.withAliases(aliases));
    binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(result));
}

虽然我们自己不必接触任何这些代码,但了解 Spring Boot 自动配置的幕后发生了什么仍然很有用。

除此之外,事务管理器和实体管理器 bean 配置与标准 Spring 应用程序相同。

7. 结论

本文是对如何配置我们的 Spring Data JPA 项目以使用多个数据库的实际概述。

完整的实现可以在 GitHub 项目中找到。这是一个基于 Maven 的项目,因此应该易于导入和按原样运行。

参考 :


https://www.baeldung.com/spring-data-jpa-multiple-databases

github:https://github.com/eugenp/tutorials/tree/master/persistence-modules/spring-data-jpa-enterprise-2

相关推荐

教你把多个视频合并成一个视频的方法

一.情况介绍当你有一个m3u8文件和一个目录,目录中有连续的视频片段,这些片段可以连成一段完整的视频。m3u8文件打开后像这样:m3u8文件,可以理解为播放列表,里面是播放视频片段的顺序。视频片段像这...

零代码编程:用kimichat合并一个文件夹下的多个文件

一个文件夹里面有很多个srt字幕文件,如何借助kimichat来自动批量合并呢?在kimichat对话框中输入提示词:你是一个Python编程专家,完成如下的编程任务:这个文件夹:D:\downloa...

Java APT_java APT 生成代码

JavaAPT(AnnotationProcessingTool)是一种在Java编译阶段处理注解的工具。APT会在编译阶段扫描源代码中的注解,并根据这些注解生成代码、资源文件或其他输出,...

Unit Runtime:一键运行 AI 生成的代码,或许将成为你的复制 + 粘贴神器

在我们构建了UnitMesh架构之后,以及对应的demo之后,便着手于实现UnitMesh架构。于是,我们就继续开始UnitRuntime,以用于直接运行AI生成的代码。PS:...

挣脱臃肿的枷锁:为什么说Vert.x是Java开发者手中的一柄利剑?

如果你是一名Java开发者,那么你的职业生涯几乎无法避开Spring。它如同一位德高望重的老国王,统治着企业级应用开发的大片疆土。SpringBoot的约定大于配置、SpringCloud的微服务...

五年后,谷歌还在全力以赴发展 Kotlin

作者|FredericLardinois译者|Sambodhi策划|Tina自2017年谷歌I/O全球开发者大会上,谷歌首次宣布将Kotlin(JetBrains开发的Ja...

kotlin和java开发哪个好,优缺点对比

Kotlin和Java都是常见的编程语言,它们有各自的优缺点。Kotlin的优点:简洁:Kotlin程序相对于Java程序更简洁,可以减少代码量。安全:Kotlin在类型系统和空值安全...

移动端架构模式全景解析:从MVC到MVVM,如何选择最佳设计方案?

掌握不同架构模式的精髓,是构建可维护、可测试且高效移动应用的关键。在移动应用开发中,选择合适的软件架构模式对项目的可维护性、可测试性和团队协作效率至关重要。随着应用复杂度的增加,一个良好的架构能够帮助...

颜值非常高的XShell替代工具Termora,不一样的使用体验!

Termora是一款面向开发者和运维人员的跨平台SSH终端与文件管理工具,支持Windows、macOS及Linux系统,通过一体化界面简化远程服务器管理流程。其核心定位是解决多平台环境下远程连接、文...

预处理的底层原理和预处理编译运行异常的解决方案

若文章对您有帮助,欢迎关注程序员小迷。助您在编程路上越走越好![Mac-10.7.1LionIntel-based]Q:预处理到底干了什么事情?A:预处理,顾名思义,预先做的处理。源代码中...

为“架构”再建个模:如何用代码描述软件架构?

在架构治理平台ArchGuard中,为了实现对架构的治理,我们需要代码+模型描述所要处理的内容和数据。所以,在ArchGuard中,我们有了代码的模型、依赖的模型、变更的模型等,剩下的两个...

深度解析:Google Gemma 3n —— 移动优先的轻量多模态大模型

2025年6月,Google正式发布了Gemma3n,这是一款能够在2GB内存环境下运行的轻量级多模态大模型。它延续了Gemma家族的开源基因,同时在架构设计上大幅优化,目标是让...

比分网开发技术栈与功能详解_比分网有哪些

一、核心功能模块一个基本的比分网通常包含以下模块:首页/总览实时比分看板:滚动展示所有正在进行的比赛,包含比分、比赛时间、红黄牌等关键信息。热门赛事/焦点战:突出显示重要的、关注度高的比赛。赛事导航...

设计模式之-生成器_一键生成设计

一、【概念定义】——“分步构建复杂对象,隐藏创建细节”生成器模式(BuilderPattern):一种“分步构建型”创建型设计模式,它将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建...

构建第一个 Kotlin Android 应用_kotlin简介

第一步:安装AndroidStudio(推荐IDE)AndroidStudio是官方推荐的Android开发集成开发环境(IDE),内置对Kotlin的完整支持。1.下载And...