Spring: 允许缺少数据库连接

Spring: Allow missing database connection

我有一个连接到多个数据库(Oracle 和 SQLServer)的项目,其中一些在启动应用程序时并不总是强制性的或不可用的 - 特别是在开发环境中。

我正在寻找一种允许应用程序在没有数据库连接的情况下启动的方法。我们只需要在执行特定任务时连接到这些数据库,这些任务使用需要特定网络访问的登台数据库,但在开发时并不总是可用。

在这里,我们的 Oracle 连接(在特定网络下的连接):

我们有一个允许应用程序启动的配置,但是在 运行 测试时由于错误的 HikariDataSource 转换而抛出错误:

@Configuration
@EnableJpaRepositories(
        basePackages = "com.bar.foo.repository.oracle",
        entityManagerFactoryRef = "oracleEntityManager",
        transactionManagerRef = "oracleTransactionManager"
)
public class OracleConfiguration {
private final Logger            log = LoggerFactory.getLogger(this.getClass());

@Inject
private Environment             environment;

@Value("${spring.datasource.oracle.ddl-auto:none}")
private String hibernateddlAuto;

@Value("${spring.datasource.oracle.dialect:org.hibernate.dialect.Oracle10gDialect}")
private String hibernateDialect;

@Bean
@Primary
@ConfigurationProperties("spring.datasource.oracle")
public DataSourceProperties oracleDataSourceProperties() {
    return new DataSourceProperties();
}

@Bean
@Primary
@ConfigurationProperties("spring.datasource.hikari")
public DataSource oracleDataSource() {
    HikariDataSource ds = (HikariDataSource) oracleDataSourceProperties().initializeDataSourceBuilder().build();
    ds.setPoolName(environment.getProperty("spring.datasource.oracle.poolName"));
    return ds;
}


@Bean
@Primary
public LocalContainerEntityManagerFactoryBean oracleEntityManager() {
    LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setPackagesToScan("com.bar.foo.domain.oracle");
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    em.setJpaVendorAdapter(vendorAdapter);
    HashMap<String, Object> properties = new HashMap<>();
    properties.put("hibernate.ddl-auto", hibernateddlAuto);
    properties.put("hibernate.dialect", hibernateDialect);
    em.setJpaPropertyMap(properties);
    em.setDataSource(oracleDataSource());
    return em;
}

@Primary
@Bean
public PlatformTransactionManager oracleTransactionManager() {

    JpaTransactionManager transactionManager


     = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(
            oracleEntityManager().getObject());
    return transactionManager;
}
}

我移动了这个配置来扩展 HikariConfig 如下:

@Configuration
@EnableJpaRepositories(
    basePackages = "com.bar.foo.repository.oracle",
    entityManagerFactoryRef = "oracleEntityManager",
    transactionManagerRef = "oracleTransactionManager"
)
@ConfigurationProperties("spring.datasource.oracle")
public class OracleConfiguration extends HikariConfig{


    @Bean
    @Primary
    public DataSource oracleDataSource() {
      return new HikariDataSource(this);
    }

    @Bean
    @Primary
public LocalContainerEntityManagerFactoryBean oracleEntityManager() {
  LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
  em.setPackagesToScan("com.bar.foo.domain.oracle");
  HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
  em.setJpaVendorAdapter(vendorAdapter);
  HashMap<String, Object> properties = new HashMap<>();
      properties.put("hibernate.ddl-auto", hibernateddlAuto);
      properties.put("hibernate.dialect", hibernateDialect);
  em.setJpaPropertyMap(properties);
  em.setDataSource(oracleDataSource());
  return em;
}

@Bean
@Primary
public PlatformTransactionManager oracleTransactionManager() {

  JpaTransactionManager transactionManager
    = new JpaTransactionManager();
  transactionManager.setEntityManagerFactory(
    oracleEntityManager().getObject());
  return transactionManager;
}

如果 Oracle 数据库不可用,则不会启动。
使用嵌入式数据库不适合我们的用例,因为我们并不总是需要这种连接,而当我们需要时,我们需要从生产中复制特定数据。 将其拆分为多个 microservice/applications 也是不行的,因为这将是一个巨大的重构,并且并不真正适合我们的用例(我们将来自多个来源的数据聚合到一个最终数据中)。

有没有简单的方法可以做到这一点?

HikariCP 提供了一些真正 nice configuration properties 可能适合您的需要。具体来说(该列表中的第一个)initializationFailTimeout:

This property controls whether the pool will "fail fast" if the pool cannot be seeded with an initial connection successfully...

A value less than zero will bypass any initial connection attempt, and the pool will start immediately while trying to obtain connections in the background. Consequently, later efforts to obtain a connection may fail.

如果您想以这种方式解决您的问题,即通过隐藏任何初始化失败(通过设置一个负 initializationFailTimeout 值),那么您只需要确保您有正确的逻辑以防万一当您从池中获取连接时,数据库为 unaccesible/down。

嗯,在仔细查看 HikariConfig class 之后,我发现了 initializationFailTimeout 表示

* @param initializationFailTimeout the number of milliseconds before the
*        pool initialization fails, or 0 to validate connection setup but continue with
*        pool start, or less than zero to skip all initialization checks and start the
*        pool without delay.

将其设置为零或以下允许应用程序启动,但它比以前花费的时间长得多,将其设置为零以下不会阻止它等待两次连接超时。

我最终还在我们的开发配置文件中将连接超时设置为最小 250 毫秒,目前看来工作正常。

另一种解决方案是设置以下参数:

spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect

使用该设置,我没有像使用 initializationFailTimeout 设置时那样遇到应用程序启动缓慢的情况。