【问题标题】:Spring boot multiple external datasource stored in internal databaseSpring Boot 多个外部数据源存储在内部数据库中
【发布时间】:2019-01-11 08:05:22
【问题描述】:

我有一个 Spring Boot 项目,并且我有一个内部数据库,其中包含 application.properties 上的配置。在这个数据库中,我有一个 Company 表,其中包含与外部数据库的连接信息(所有外部数据库都具有相同的结构)。

我创建了一个在需要时创建数据源的类:

public class PgDataSource {

    private static Map<Long, DataSource> dataSourceMap = new HashMap<>();

    private static void createDataSource(Company company) {
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setMaximumPoolSize(10);
        hikariConfig.setMinimumIdle(1);
        hikariConfig.setJdbcUrl("jdbc:postgresql://"+company.getUrl()+"/"+company.getIdClient());
        hikariConfig.setUsername(company.getUsername());
        hikariConfig.setPassword(company.getPassword());

        dataSourceMap.put(company.getId(), new HikariDataSource(hikariConfig));
    }

    public static DataSource getDataSource(Company company) {
        if (!dataSourceMap.containsKey(company.getId()))
            createDataSource(company);

        return dataSourceMap.get(company.getId());
    }

}

你能告诉我这个解决方案是否是最好的,我是否可以在这个解决方案中使用 JPA?

谢谢

【问题讨论】:

    标签: java spring spring-boot jpa


    【解决方案1】:

    设置的困难不是多个数据源,而是它们是动态的,即在运行时确定的事实。 除了DataSource,JPA 还使用EntityManagerFactoryTransactionManager,它们是静态确定的,即在编译时。因此,将 JPA 与动态数据源一起使用并不容易。

    在 Spring Boot 2 中,您可以尝试 AbstractRoutingDataSource,它允许基于某些(线程绑定)上下文将 JPA 调用路由到不同的数据源。 Here's 一个如何使用它的示例和一个 demo 应用程序。

    或者,您可以将您的设置转换为静态设置,然后使用常规的multiple datasource 方法。缺点是“公司”列表将在编译时固定,因此可能不是您想要的。

    【讨论】:

      【解决方案2】:

      谢谢它的工作正常!

      我的解决方案:

      我使用@Primary 注释为我的本地数据库创建了第一个数据源。

      @Configuration
      @EnableTransactionManagement
      @EnableJpaRepositories(
          entityManagerFactoryRef = "localEntityManagerFactory",
          basePackages = {"fr.axygest.akostaxi.local"}
      )
      public class LocalConfig {
      
          @Primary
          @Bean(name = "dataSource")
          @ConfigurationProperties(prefix = "spring.datasource")
          public DataSource dataSource() {
              return DataSourceBuilder.create().build();
          }
      
          @Primary
          @Bean(name = "localEntityManagerFactory")
          public LocalContainerEntityManagerFactoryBean entityManagerFactory(
                  EntityManagerFactoryBuilder builder,
                  @Qualifier("dataSource") DataSource dataSource) {
      
              return builder
                      .dataSource(dataSource)
                      .packages("fr.axygest.akostaxi.local.model")
                      .persistenceUnit("local")
                      .build();
          }
      
          @Primary
          @Bean(name = "transactionManager")
          public PlatformTransactionManager transactionManager(
                  @Qualifier("localEntityManagerFactory") EntityManagerFactory
                          entityManagerFactory
          ) {
              return new JpaTransactionManager(entityManagerFactory);
          }
      
      }
      

      接下来,对于保存在本地数据库company表中的x个外部数据库,我使用AbstractRoutingDataSource

      我将当前上下文存储为 ThreadLocal :

      public class ThreadPostgresqlStorage {
      
          private static ThreadLocal<Long> context = new ThreadLocal<>();
      
          public static void setContext(Long companyId) {
              context.set(companyId);
          }
      
          public static Long getContext() {
              return context.get();
          }
      
      }
      

      我定义了 RoutingSource 来扩展 AbstractRoutingDataSource

      public class RoutingSource extends AbstractRoutingDataSource
      {
          @Override
          protected Object determineCurrentLookupKey() {
              return ThreadPostgresqlStorage.getContext();
          }
      
      }
      

      配置类创建保存在公司表中的所有数据库连接:

      @Configuration
      @EnableJpaRepositories(
              basePackages = {"fr.axygest.akostaxi.postgresql"},
              entityManagerFactoryRef = "pgEntityManager"
      )
      @EnableTransactionManagement
      public class PgConfig {
      
          private final CompanyRepository companyRepository;
      
          @Autowired
          public PgConfig(CompanyRepository companyRepository) {
              this.companyRepository = companyRepository;
          }
      
          @Bean(name = "pgDataSource")
          public DataSource pgDataSource() {
              RoutingSource routingSource = new RoutingSource();
              List<Company> companies = companyRepository.findAll();
              HashMap<Object, Object> map = new HashMap<>(companies.size());
              companies.forEach(company -> {
                  map.put(company.getId(), createDataSource(company));
              });
              routingSource.setTargetDataSources(map);
              routingSource.afterPropertiesSet();
              return routingSource;
          }
      
          @Bean(name = "pgEntityManager")
          public LocalContainerEntityManagerFactoryBean pgEntityManager(
                  EntityManagerFactoryBuilder builder,
                  @Qualifier("pgDataSource") DataSource dataSource) {
      
              return builder
                      .dataSource(dataSource)
                      .packages("fr.axygest.akostaxi.postgresql.model")
                      .persistenceUnit("pg")
                      .properties(jpaProperties())
                      .build();
          }
      
          private DataSource createDataSource(Company company) {
              HikariConfig hikariConfig = new HikariConfig();
              hikariConfig.setMaximumPoolSize(10);
              hikariConfig.setMinimumIdle(1);
              hikariConfig.setJdbcUrl("jdbc:postgresql://" + company.getUrl() + "/" + company.getIdClient());
              hikariConfig.setUsername(company.getUsername());
              hikariConfig.setPassword(company.getPassword());
      
              return new HikariDataSource(hikariConfig);
          }
      
          private Map<String, Object> jpaProperties() {
              Map<String, Object> props = new HashMap<String, Object>();
              props.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
      
              return props;
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-01
        • 1970-01-01
        • 2014-03-19
        • 2019-12-10
        • 2018-08-18
        • 1970-01-01
        相关资源
        最近更新 更多