如何配置 Spring boot 以使用两个数据库?

2024-06-30

我在用春季启动 2.X with 休眠5连接两个不同的 MySQL 数据库(Bar 和 Foo)在不同的服务器上。我试图列出一个实体的所有信息(自己的属性和@OneToMany and @ManyToOne关系)来自 REST 控制器中的方法。

我已经遵循了几个教程来做到这一点,因此,我能够获得我的所有信息@Primary数据库(Foo),但是,在检索辅助数据库(Bar)时,我总是会遇到异常@OneToMany套。如果我交换@Primary对 Bar 数据库进行注释,我能够从 Bar 数据库获取数据,但不能从 Foo 数据库获取数据。有办法解决这个问题吗?

这是我得到的异常:

...w.s.m.s.DefaultHandlerExceptionResolver :
Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: 
    Could not write JSON document: failed to lazily initialize a collection of role: 
        com.foobar.bar.domain.Bar.manyBars, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]-com.foobar.bar.domain.Bar["manyBars"]); 
    nested exception is com.fasterxml.jackson.databind.JsonMappingException:
        failed to lazily initialize a collection of role: 
        com.foobar.bar.domain.Bar.manyBars, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->com.foobar.bar.domain.Bar["manyBars"])

我的应用程序属性:

# MySQL DB - "foo"
spring.datasource.url=jdbc:mysql://XXX:3306/foo?currentSchema=public
spring.datasource.username=XXX
spring.datasource.password=XXX
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# MySQL DB - "bar"
bar.datasource.url=jdbc:mysql://YYYY:3306/bar?currentSchema=public
bar.datasource.username=YYYY
bar.datasource.password=YYYY
bar.datasource.driver-class-name=com.mysql.jdbc.Driver
# JPA
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect

My @Primary数据源配置:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory",
        transactionManagerRef = "transactionManager",
        basePackages = {"com.foobar.foo.repo"})
public class FooDbConfig {

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

    @Primary
    @Bean(name = "entityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            EntityManagerFactoryBuilder builder, @Qualifier("dataSource") DataSource dataSource) {
        return builder
                .dataSource(dataSource)
                .packages("com.foobar.foo.domain")
                .persistenceUnit("foo")
                .build();
    }

    @Primary
    @Bean(name = "transactionManager")
    public PlatformTransactionManager transactionManager(
            @Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}

我的辅助数据源配置:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "barEntityManagerFactory",
        transactionManagerRef = "barTransactionManager", basePackages = {"com.foobar.bar.repo"})
public class BarDbConfig {

    @Bean(name = "barDataSource")
    @ConfigurationProperties(prefix = "bar.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "barEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean barEntityManagerFactory(
            EntityManagerFactoryBuilder builder, @Qualifier("barDataSource") DataSource dataSource) {
        return builder
                .dataSource(dataSource)
                .packages("com.foobar.bar.domain")
                .persistenceUnit("bar")
                .build();
    }

    @Bean(name = "barTransactionManager")
    public PlatformTransactionManager barTransactionManager(
            @Qualifier("barEntityManagerFactory") EntityManagerFactory barEntityManagerFactory) {
        return new JpaTransactionManager(barEntityManagerFactory);
    }
}

REST 控制器类:

@RestController
public class FooBarController {

    private final FooRepository fooRepo;
    private final BarRepository barRepo;

    @Autowired
    FooBarController(FooRepository fooRepo, BarRepository barRepo) {
        this.fooRepo = fooRepo;
        this.barRepo = barRepo;
    }

    @RequestMapping("/foo")
    public List<Foo> listFoo() {
        return fooRepo.findAll();
    }

    @RequestMapping("/bar")
    public List<Bar> listBar() {
        return barRepo.findAll();
    }

    @RequestMapping("/foobar/{id}")
    public String fooBar(@PathVariable("id") Integer id) {
        Foo foo = fooRepo.findById(id);
        Bar bar = barRepo.findById(id);

        return foo.getName() + " " + bar.getName() + "!";
    }

}

Foo/Bar 存储库:

@Repository
public interface FooRepository extends JpaRepository<Foo, Long> {
  Foo findById(Integer id);
}

@Repository
public interface BarRepository extends JpaRepository<Bar, Long> {
  Bar findById(Integer id);
}

实体为@Primary数据源。第二个数据源的实体是相同的(仅更改类名):

@Entity
@Table(name = "foo")
public class Foo {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    private Integer id;

    @Column(name = "name")
    private String name;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "foo")
    @JsonIgnoreProperties({"foo"})
    private Set<ManyFoo> manyFoos = new HashSet<>(0);

    // Constructors, Getters, Setters
}

@Entity
@Table(name = "many_foo")
public class ManyFoo {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    private Integer id;

    @Column(name = "name")
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JsonIgnoreProperties({"manyFoos"})
    private Foo foo;

    // Constructors, Getters, Setters
}  

最后,我的应用程序主要:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

值得注意的是,该解决方案应保留两个数据库的 Lazy 属性,以保持最佳性能。

Edit 1:如果两个目录(MySQL 术语中的“数据库”)位于同一个数据库(“服务器”)中,则 Rick James 解决方案有效!

问题依然存在当目录(MySQL 数据库)位于不同的数据库(服务器)中并且尝试保持 Lazy 属性时

非常感谢。


*在 Hibernate 和 JPA 中,ToMany 集合默认是惰性的。该错误是因为 Jackson 试图在实体管理器(在休眠状态下称为会话)关闭时序列化 OneToMany。因此,惰性集合无法被检索。

Spring Boot with JPA 默认提供OpenEntityManagerInViewFilter对于初级 EM。这允许只读数据库访问,但默认情况下仅适用于主 EM。

您有 3 个选择:

1)您可以添加连接获取,例如FetchMode 在 Spring Data JPA 中如何工作 https://stackoverflow.com/questions/29602386/how-does-the-fetchmode-work-in-spring-data-jpa#29667050

2) 您可以为非主要实体管理器添加 OpenEntityManagerInViewFilter 并将其添加到您的上下文中。

请注意,这意味着一个挑战,对于每个 Bar 和 Foo 实例,您的应用程序将返回数据库以检索 OneToMany。这部分不适用于 Bar,但适用于 Foo。这意味着存在可扩展性问题(有些人称之为 N + 1 问题),因为对于每个 foo 和 bar,您都运行一个额外的查询,对于大量的 Foos 和 Bars 来说,这会变得很慢。

3) 另一种方法是让您在 Bar 和 Foo 上的收藏变得渴望(请参阅此https://docs.oracle.com/javaee/7/api/javax/persistence/OneToMany.html#fetch-- https://docs.oracle.com/javaee/7/api/javax/persistence/OneToMany.html#fetch--)但如果​​可扩展性是您所关心的问题,则需要仔细分析。

我推荐选项#1。

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

如何配置 Spring boot 以使用两个数据库? 的相关文章

  • 如何在 JPA/Hibernate 中找出实体是否分离?

    有没有办法查询JPA实体管理器给定实体是否分离 这个帖子 https stackoverflow com questions 2779857 how to know if a detached jpa entity has already
  • 当不读取带有 URL 的 QR 码时,zxing QRCodeReader 中出现 ChecksumException

    如果我扫描带有 URL 的 QR 码 以下代码可以完美且快速地运行 然而 如果我用简单的字符串或数字序列解码 QR 码 这就是我想要做的 它有时会随机工作 但 99 的情况下它会失败并出现 ChecksumException if webc
  • 使用 == 比较 Long 对象类型和原始 int

    我有一个通过调用返回 Long 对象数据类型的方法 resp getResultCode 我想比较一下HttpStatus GONE value 它实际上只返回一个原始 int 值410 Long 会拆箱自身以正确地与 int 原语进行比较
  • 使用递归求数字之和

    对Java来说相当陌生 但我正在编写一个教程 其中我必须使用递归找到用户输入整数的数字总和 到目前为止 这是我的代码 公开课其他 public static void main String arg Scanner s new Scanne
  • Mac 上缺少 spring-boot-starter-web hibernate-validator 依赖项

    我在STS Spring工具套件 上创建了Spring Boot项目 Spring Starter Project Window和Mac都有 这是我的pom xml
  • 使用 Jackson 反序列化非字符串映射键

    我有一张如下所示的地图 public class VerbResult JsonProperty similarVerbs private Map
  • 在可分页资源上生成自链接时出错

    制作一个简单的 RestController RestController public class Controloler Value class MyData int value GetMapping value datas produ
  • 在MySQL中保存使用触发器删除记录的用户的USER_ID

    我正在尝试设置一系列历史记录触发器 以通过触发器自动收集给定表的历史记录 我想使用触发器 因为这可以保证我捕获所有更改 无论是否有人忘记将其保存在应用程序中 我的问题是我有这个触发器 CREATE TRIGGER db delete his
  • 使用 Flink 监控数据流目录中的新文件

    我目前正在编写我的第一个 Flink 应用程序 并且想要监视文件夹中的新文件 不幸的是我找不到关于这个主题的很多例子 我找到了readFile fileInputFormat path watchType interval pathFilt
  • spring - 构造函数注入和重写嵌套 bean 的父级定义

    我读过 Spring 3 参考资料继承bean定义 http static springsource org spring docs 3 0 x spring framework reference html beans html bean
  • 在 Eclipse 中默认将类设为 Final 吗?

    有没有办法让 Eclipse 中的类默认为final 即 在保存操作中 还是在创建新类对话框中 这确实是一个 hack 但是您可以在 Eclipse 设置中设置以下代码模板 在 Java gt 代码样式 gt 代码模板 gt 代码 gt 新
  • JPA 多对多关系创建两个联接表

    我正在尝试在之间创建多对多关系User and FileObject假设用户可以访问许多文件对象的类 并且文件对象可以由许多用户和一对多关系访问 因为一个用户可以拥有许多文件 但一个文件只能由一个用户拥有 这是我的代码 Entity pub
  • 如何将“书”添加到/shelves/1/books

    我不知道如何做一些本应非常简单的事情 我有两个实体 书架和书 一个书架可以放置一本或多本书 这些实体中的每一个都有一个相应的 JpaRepository 使用 Spring Data Rest 作为剩余存储库公开 当我运行该应用程序时 所有
  • 我正在从数组列表中获取内存地址,需要信息

    我正在获取一个文本文件并填充一个数组列表 为了测试该文件 我在继续之前将其打印出来 我只能看到内存地址 而看不到文件中的实际信息 我缺少一些简单且可能明显的东西吗 public class TriviaQuestion private St
  • 使用 Spring Security 标记库时将异常映射到 404 页面

    将异常映射到 404 页面时 Spring Security 标记无法从安全上下文中找到身份验证信息 通过 真实 404 可以找到身份验证 我的 web xml
  • Moodle 2.0 与 Nginx 后端

    您好 我正在寻找有关如何为 Moodle 2 0 配置服务器 以 nginx 作为服务器 以 PHP FPM 或 FastCGI 以 mySQL 作为后端 的教程 抱歉 如果我对服务器架构菜鸟的这些术语感到困惑 可能会在 Ubuntu De
  • 将.IBD文件导入MySQL服务器

    我正在尝试找到一种方法将 旧复制的 ibd 文件导入到新安装的 MYSQL Server 8 0 中 您需要了解exact表的结构 CREATE TABLE陈述 创建具有相同结构的表 Run ALTER TABLE table name D
  • 如何使用 Firebase 查询中的信息填充 Android ListView

    这是我的第一篇文章 所以如果我没有遵循我应该遵循的一些协议 我深表歉意 我正在尝试使用 Firebase 数据库中的一些信息填充 ListView 我认为我遇到的问题是对数据库的查询太慢 线程可能正在下载图片 并且我的活动加载其活动布局而不
  • 如何确定 MySQL 中某些查询的优先级?

    我对一个高度竞争的 MySQL 数据库有大量的后台读取和写入 而用户读取的数量要少得多 有没有办法将某些查询 用户查询 标记为高优先级 以便它们优先于后台查询 我希望用户响应能力较高 但并不真正关心后台查询 Thanks MySQL支持in
  • Java 8 并行流和 ThreadLocal

    我试图弄清楚如何在 Java 8 并行流中复制 ThreadLocal 值 所以如果我们考虑一下 public class ThreadLocalTest public static void main String args Thread

随机推荐