Spring Data JPA - 多个 EnableJpaRepositories

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/45663025/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 01:29:09  来源:igfitidea点击:

Spring Data JPA - Multiple EnableJpaRepositories

springhibernatejpaspring-data-jpadatasource

提问by Ansar Samad

My application has multiple data sources , so i have created two data source configuration classes based on this URL.

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

But while running the spring boot application am getting error

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

Description: Field userDataRepo in com.cavion.services.UserDataService required a bean named 'entityManagerFactory' that could not be found. Action: Consider defining a bean named 'entityManagerFactory' in your configuration.

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

From this Questionon StackOverflow helped me to figure out the issue.i need to specify the entityManagerFactoryRef on my JPA repositories .

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

But i have many repository classes some of them uses Entitymanager 'A' and some of them uses 'B' . my current spring boot application class is like this

但是我有很多存储库类,其中一些使用 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);
        }

    };
}}

I have given the EnableJpaRepositories on the spring boot class , so how can i configure multiple EnableJpaRepositories so that i can configure multiple entityManagerFactory ?

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

Please suggest the best way to setup the multiple data sources .

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

回答by Daniel C.

In order to let spring knows what DataSourceis related to what Repositoryyou should define it at the @EnableJpaRepositoriesannotation. Let's assume that we have two entities, the Serversentity and the Domainsentity and each one has its own Repo then each Repository has its own JpaDataSource configuration.

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

1. Group all the repositories based on the Data Source that they are related to. For example

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

Repository for Domainsentities (package: org.springdemo.multiple.datasources.repository.domains):

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> {
}

Repository for Serversentities (package: org.springdemo.multiple.datasources.repository.servers)

存储库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. For each JPA Data Soruce you need to define a configuration, in this example I show how to configure two different DataSources

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

DomainsJpa Configuration: the relationship between the Data Source and the repository is defined in the basePackagesvalue, that is the reason why is necessary to group the repositories in different packages depending on the entity manager that each repo will use.

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

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

ServersData Source Configuration: as you can see the basePackages value has the package name of the ServersRepository , and also the values of entityManagerFactoryRefand transactionManagerRefare different in order to let spring separate each entityManager.

Servers数据源配置:正如你所看到的basePackages值具有的包名Servers库,并且还值entityManagerFactoryReftransactionManagerRef是为了让春天分隔每个EntityManager的不同。

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

3. Set one Datasource as primary

3. 设置一个数据源为主

In order to avoid the error message: Parameter 0 of constructor in org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration required a single bean, but 2 were found:just set one of the datasource as @Primary, in this example I select the ServersDatasource as primary:

为了避免错误消息: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();
}

If you need more information please see the full example for each configuration:

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

ServersJPA Configuration

ServersJPA 配置

@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;
    }
}

DomainsJPA Configuration

DomainsJPA 配置

@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;
    }

}

In order to separate each datasource I put the configuration in the application.propertiesfile, like this:

为了分离每个数据源,我将配置放在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

If you need more information please see the following documentation:

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

Spring Documentation: howto-two-datasources

Spring 文档:howto-two-datasources

A similar example of how configure two different databases: github example

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

回答by S.P.

The answered provided by @Daniel C. is correct. Small correction/observation from my side.

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

  • @Primaryis not required if you don't want to mark any datasource as default one, otherwise necessary.
  • If you are defining any of the EntityManagerFactoryBeanwith @Beanname as entityManagerFactorythen it's better to mark it @Primaryto avoid conflict.
  • @ConfigurationProperties("app.datasource.servers") can be marked at class level instead of defining at method level.
  • Better to return HikariDataSourceas datasource if you using Spring Boot 2.x or higher version as it has been changed.
  • Make sure you define exact property for jdbc-urlwhich is being used by HikariDataSourceto refer JDBC Connection URL.
  • 如果您不想将任何数据源标记为默认数据源,则不需要@Primary,否则是必需的。
  • 如果您使用@Bean名称将任何EntityManagerFactoryBean定义为entityManagerFactory,那么最好将其标记为@Primary以避免冲突。
  • @ConfigurationProperties("app.datasource.servers") 可以在类级别进行标记,而不是在方法级别进行定义。
  • 如果您使用 Spring Boot 2.x 或更高版本,因为它已更改,最好将HikariDataSource作为数据源返回。
  • 请确保您定义确切性质JDBC的网址正在使用由 HikariDataSource指JDBC连接的URL。