当前位置: 首页 > 知识库问答 >
问题:

如何配置Spring Boot工作与两个数据库?

司马洲
2023-03-14

我正在使用Spring Boot 2。使用Hibernate 5连接不同服务器上的两个不同的MySQL数据库(Bar和Foo)。我试图从REST Controller中的方法列出实体的所有信息(自己的属性和@OneToOne和@ManyToOne关系)。

我已经遵循了几个教程来做到这一点,因此,我能够获得我的@主数据库(Foo)的所有信息,但是,当检索@OneTo很多集时,我总是得到我的辅助数据库(Bar)的异常。如果我将@主注释交换到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

我的@主DataSource配置:

@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属性,以保持最佳性能。

编辑1:如果两个目录(MySQL术语中的“数据库”)在同一个数据库(“服务器”)中,Rick James解决方案可以工作!!

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

非常感谢。

共有2个答案

姜景焕
2023-03-14

同一服务器上的两个数据库(又名“目录”)?只使用一个连接。然后这样引用:

Foo.table1
Bar.table2

只要有一个简单的表名,就可以使用该语法。

不同的服务器

如果数据不在同一台机器上,就会变得混乱。有几个想法:

  • 从每个目录中获取数据,然后在应用程序代码中进行操作。框架可能没有钩子可以同时对两台服务器执行任何操作
东方嘉木
2023-03-14

*在Hibernate中,默认情况下,许多集合都是惰性的

默认情况下,带有JPA的Spring Boot为主EM提供了一个OpenEntityManagerInViewFilter。这允许只读DB访问,但默认情况下仅适用于主EM。

你有3个选择:

1) 您可以添加连接获取,例如,在SpringDataJPA中,获取模式是如何工作的

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

请注意,这意味着一个挑战,对于每个Bar和Foo实例,您的应用程序将返回到数据库以检索OneToMany。这一部分不适用于Bar,但适用于Foo。这意味着一个可伸缩性问题(有些人称之为n1问题),对于每个foo和bar,您运行一个额外的查询,对于数量不多的foo和bar,查询速度会变慢。

3) 另一种方法是让你在酒吧和美食上的收藏变得更加迫切(见下图)https://docs.oracle.com/javaee/7/api/javax/persistence/OneToMany.html#fetch--)但如果您担心可伸缩性,则需要仔细分析这一点。

我推荐选项1。

 类似资料:
  • 我正在为Spring Core认证学习,对于使用Java配置方式配置Bean的相关练习,我有以下疑问。 Java配置的正确解释是Spring吗? 例如,我可以说RewardNetwork是声明的bean,而RewardNetworkImpl是这个bean的当前实现吗? 所有的3Beans(AccountRepository,RestaurantRepository和RewardRepository

  • 我有一个简单的SpringBoot应用程序,我想使用AutoConfiguration来配置Tomcat jdbc池数据源。 我正在使用这些Spring依赖项: 以下是我的 application.yml 文件中的数据源属性: 我确定正在加载属性,因为应用程序正在获取其他值。 我在配置文件中将 bean 定义为: 我将数据源注入到我的DAO中,如下所示: 如果我在getDataSource()方法

  • 问题内容: 我有一份詹金斯工作,后来被克隆和修改。现在,我想比较两个作业的配置。不是历史更改,不是结果,而是两个作业的配置。 是否可以比较两个Jenkins作业的配置? 问题答案: 尝试: 要么

  • 我正在处理一个Flask项目,我正在使用Flask SQLAlchemy。 我需要处理多个已经存在的数据库。 我创建了“app”对象和SQLAlchemy对象: 在配置中,我设置了默认连接和附加绑定: 然后,我使用声明性系统创建了表模型,并在需要时设置参数以指示表位于哪个数据库中。 例如: 通过这种方式,当我在正确的数据库上进行查询时,一切都正常工作。 阅读SQLAlchemy文档和Flask S

  • 'application.screenupdating=True Dim WbA As Workbook Set WbA=thisworkbook 在此输入图像描述

  • 我正在尝试在Spring云数据流中配置DLQ。下面是流定义以及我如何部署它 在自定义转换处理器代码中,我已经提到过 这意味着若消息包含错误,那个么RunTimeException和我想在DLQ中捕获这些消息。但当我运行代码时,似乎没有得到任何名为test tran的Kafka DL队列。 我是否需要设置更多属性来启用DLQ,还是需要更改代码中的某些内容以正确使用DLQ。 自定义转换代码 Trans