Spring Data JPA - 多个 EnableJpaRepositories

新手上路,请多包涵

我的应用程序有多个数据源,所以我根据这个 URL 创建了两个数据源配置类。

但是在运行 spring boot 应用程序时出现错误

说明:com.cavion.services.UserDataService 中的字段 userDataRepo 需要一个名为“entityManagerFactory”的 bean,但找不到。操作:考虑在您的配置中定义一个名为“entityManagerFactory”的 bean。

从 StackOverflow 上的这个 问题 帮助我弄清楚了问题。我需要在我的 JPA 存储库上指定 entityManagerFactoryRef 。

但是我有很多存储库类,其中一些使用 Entitymanager ‘A’,一些使用 ‘B’。我当前的 spring boot 应用程序类是这样的

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
    DataSourceTransactionManagerAutoConfiguration.class })
@EnableTransactionManagement
@EntityScan("com.info.entity")
@ComponentScan({"com.info.services","com.info.restcontroller"})
@EnableJpaRepositories("com.info.repositories")
public class CavionApplication {

public static void main(String[] args) {
    SpringApplication.run(CavionApplication.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
    return args -> {

        System.out.println("Let's inspect the beans provided by Spring Boot:");

        String[] beanNames = ctx.getBeanDefinitionNames();
        Arrays.sort(beanNames);
        for (String beanName : beanNames) {
            System.out.println(beanName);
        }

    };
}}

我已经在 spring boot 类上提供了 EnableJpaRepositories,那么如何配置多个 EnableJpaRepositories 以便我可以配置多个 entityManagerFactory?

请建议设置多个数据源的最佳方法。

原文由 Ansar Samad 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 829
2 个回答

为了让 spring 知道什么 DataSource 与什么 Repository --- 相关,你应该在 @EnableJpaRepositories 注释中定义它。假设我们有两个实体, Servers 实体和 Domains 实体,每个实体都有自己的 Repo,然后每个 Repository 都有自己的 JpaDataSource 配置。

1. 根据相关的数据源对所有存储库进行分组。例如

Domains 实体的存储库(包: org.springdemo.multiple.datasources.repository.domains ):

 package org.springdemo.multiple.datasources.repository.domains;

import org.springdemo.multiple.datasources.domain.domains.Domains;
import org.springframework.data.jpa.repository.JpaRepository;

public interface DomainsRepository extends JpaRepository<Domains,Long> {
}

Servers 实体的存储库(包: org.springdemo.multiple.datasources.repository.servers

 package org.springdemo.multiple.datasources.repository.servers;

import org.springdemo.multiple.datasources.domain.servers.Servers;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ServersRepository extends JpaRepository<Servers,Long> {
}

2.对于每个JPA Data Source你需要定义一个配置,在这个例子中我展示了如何配置两个不同的DataSources

Domains Jpa 配置:数据源和存储库之间的关系在 basePackages 值中定义,这就是为什么需要根据实体将存储库分组到不同包中的原因每个回购将使用的经理。

 @Configuration
@EnableJpaRepositories(
        entityManagerFactoryRef = "domainsEntityManager",
        transactionManagerRef = "domainsTransactionManager",
        basePackages = {"org.springdemo.multiple.datasources.repository.domains"}
        )
public class DomainsConfig {

Servers Data Source Configuration: as you can see the basePackages value has the package name of the Servers Repository , and also the values of entityManagerFactoryRef and transactionManagerRef 不同是为了让spring把每个entityManager分开。

 @Configuration
@EnableJpaRepositories(
        entityManagerFactoryRef = "serversEntityManager",
        transactionManagerRef = "serversTransactionManager",
        basePackages = {"org.springdemo.multiple.datasources.repository.servers"}
        )
public class ServersConfig {

3.将一个数据源设置为主

为了避免错误消息: Parameter 0 of constructor in org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration required a single bean, but 2 were found: 只需将其中一个数据源设置为@Primary,在本例中我选择 Servers 数据源作为主要数据源:

 @Bean("serversDataSourceProperties")
@Primary
@ConfigurationProperties("app.datasource.servers")
public DataSourceProperties serversDataSourceProperties(){
    return new DataSourceProperties();
}

@Bean("serversDataSource")
@Primary
@ConfigurationProperties("app.datasource.servers")
public DataSource serversDataSource(@Qualifier("serversDataSourceProperties") DataSourceProperties serversDataSourceProperties) {
    return serversDataSourceProperties().initializeDataSourceBuilder().build();
}

如果您需要更多信息,请参阅每个配置的完整示例:

Servers JPA配置

@Configuration
@EnableJpaRepositories(
        entityManagerFactoryRef = "serversEntityManager",
        transactionManagerRef = "serversTransactionManager",
        basePackages = {"org.springdemo.multiple.datasources.repository.servers"}
        )
public class ServersConfig {

    @Bean(name = "serversEntityManager")
    public LocalContainerEntityManagerFactoryBean getServersEntityManager(EntityManagerFactoryBuilder builder,
                                                                          @Qualifier("serversDataSource") DataSource serversDataSource){

        return builder
                .dataSource(serversDataSource)
                .packages("org.springdemo.multiple.datasources.domain.servers")
                .persistenceUnit("servers")
                .properties(additionalJpaProperties())
                .build();

    }

    Map<String,?> additionalJpaProperties(){
        Map<String,String> map = new HashMap<>();

        map.put("hibernate.hbm2ddl.auto", "create");
        map.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
        map.put("hibernate.show_sql", "true");

        return map;
    }

    @Bean("serversDataSourceProperties")
    @Primary
    @ConfigurationProperties("app.datasource.servers")
    public DataSourceProperties serversDataSourceProperties(){
        return new DataSourceProperties();
    }

    @Bean("serversDataSource")
    @Primary
    @ConfigurationProperties("app.datasource.servers")
    public DataSource serversDataSource(@Qualifier("serversDataSourceProperties") DataSourceProperties serversDataSourceProperties) {
        return serversDataSourceProperties().initializeDataSourceBuilder().build();
    }

    @Bean(name = "serversTransactionManager")
    public JpaTransactionManager transactionManager(@Qualifier("serversEntityManager") EntityManagerFactory serversEntityManager){
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(serversEntityManager);

        return transactionManager;
    }
}

Domains JPA配置

@Configuration
@EnableJpaRepositories(
        entityManagerFactoryRef = "domainsEntityManager",
        transactionManagerRef = "domainsTransactionManager",
        basePackages = {"org.springdemo.multiple.datasources.repository.domains"}
        )
public class DomainsConfig {

    @Bean(name = "domainsEntityManager")
    public LocalContainerEntityManagerFactoryBean getdomainsEntityManager(EntityManagerFactoryBuilder builder
    ,@Qualifier("domainsDataSource") DataSource domainsDataSource){

        return builder
                .dataSource(domainsDataSource)
                .packages("org.springdemo.multiple.datasources.domain.domains")
                .persistenceUnit("domains")
                .properties(additionalJpaProperties())
                .build();

    }

    Map<String,?> additionalJpaProperties(){
        Map<String,String> map = new HashMap<>();

        map.put("hibernate.hbm2ddl.auto", "create");
        map.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
        map.put("hibernate.show_sql", "true");

        return map;
    }

    @Bean("domainsDataSourceProperties")
    @ConfigurationProperties("app.datasource.domains")
    public DataSourceProperties domainsDataSourceProperties(){
        return new DataSourceProperties();
    }

    @Bean("domainsDataSource")
    public DataSource domainsDataSource(@Qualifier("domainsDataSourceProperties") DataSourceProperties domainsDataSourceProperties) {
        return domainsDataSourceProperties.initializeDataSourceBuilder().build();
    }

    @Bean(name = "domainsTransactionManager")
    public JpaTransactionManager transactionManager(@Qualifier("domainsEntityManager") EntityManagerFactory domainsEntityManager){
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(domainsEntityManager);

        return transactionManager;
    }

}

为了分离每个数据源,我将配置放在 application.properties 文件中,如下所示:

 app.datasource.domains.url=jdbc:h2:mem:~/test
app.datasource.domains.driver-class-name=org.h2.Driver

app.datasource.servers.driver-class-name=com.mysql.jdbc.Driver
app.datasource.servers.url=jdbc:mysql://localhost:3306/v?autoReconnect=true&useSSL=false
app.datasource.servers.username=myuser
app.datasource.servers.password=mypass

如果您需要更多信息,请参阅以下文档:

Spring 文档:howto-two-datasources

如何配置两个不同数据库的类似示例: github example

原文由 Daniel C. 发布,翻译遵循 CC BY-SA 3.0 许可协议

@Daniel C. 提供的答案是正确的。我这边的小修正/观察。

  • 如果您不想将任何数据源标记为默认数据源,则不需要 @Primary ,否则是必需的。
  • 如果您将任何带有 @Bean 名称的 EntityManagerFactoryBean 定义为 entityManagerFactory ,那么最好将其标记为 @Primary 以避免冲突。
  • @ConfigurationProperties(“app.datasource.servers”) 可以在类级别标记,而不是在方法级别定义。
  • 如果您使用 Spring Boot 2.x 或更高版本,最好将 HikariDataSource 作为数据源返回,因为它已更改。
  • 确保为 HikariDataSource 用来引用 JDBC 连接 URL 的 jdbc-url 定义了确切的属性。

原文由 S.P. 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题