首页 > 解决方案 > MultiTenant Hibernate + Spring boot - 如何防止它在服务器启动期间查找连接

问题描述

我们正在使用多租户休眠 + Spring boot,如休眠中的多租户中所述。我们在创建实体管理器时提供了 MultiTenantConnectionProvider 和 CurrentTenantIdentifierResolver(请参阅下面的示例)。

在我们的 MultiTenantConnectionProvider 中,租户的目标数据库不是静态配置的。相反,它通过调用另一个服务来动态确定当前租户的目标数据库详细信息,然后为其创建数据源。

现在,我们面临一个问题,即当启动 Spring Boot 服务器时,Hibernate 正在调用 MultiTenantConenctionProvider.getAnyConnection() 方法。在服务器启动时,上下文中没有租户,因此我们没有任何数据库可以为其创建连接。此外,我们的设置中没有任何可以在服务器启动时使用的虚拟数据库

  1. 如果我们返回 null,则服务器启动失败并返回 NullPointerException
  2. 如果我们重写 anyConnection() 方法并抛出异常(如下面的代码所示),服务器只会记录异常并继续服务器启动。

有了 2,一切正常,我们能够取得进展。但是,我们真的想摆脱每次重新启动 Spring Boot 服务器时记录的这种不必要的异常。

我们正在寻找一种方法来关闭在服务器启动期间尝试连接到数据库的休眠行为,尤其是在设置休眠的多租户策略时。对于多租户软件,此行为不一致

有什么办法可以防止休眠在服务器启动期间尝试获取连接(即调用 MultiTenantConenctionProvider.getAnyConnection())?

请指教

以下是我们的 Spring 配置示例

@Configuration
@EnableTransactionManagement
public class MultiTenantHibernateJpaConfig {

    @Autowired
    private JpaProperties jpaProperties;

    @Bean
    public MultiTenantConnectionProvider multiTenantConnectionProvider() {
        return new MultiTenantConnectionProviderImpl();
    }

    @Bean
    public CurrentTenantIdentifierResolver currentTenantIdentifierResolver() {
        return new CurrentTenantIdentifierResolverImpl();
    }

    @Value("${jpaconfig.packages-to-scan:#{null}}")
    private String packagesToScan;

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(MultiTenantConnectionProvider multiTenantConnectionProvider,
                                                                       CurrentTenantIdentifierResolver currentTenantIdentifierResolver) {

        Map<String, Object> hibernateProps = new LinkedHashMap<>();
        hibernateProps.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
        hibernateProps.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProvider);
        hibernateProps.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolver);

        hibernateProps.put("javax.persistence.schema-generation.database.action", "none");
        hibernateProps.put("javax.persistence.query.timeout", "5");
        hibernateProps.put(Environment.DIALECT, "org.hibernate.dialect.Oracle10gDialect");
        hibernateProps.putAll(this.jpaProperties.getProperties());

        LocalContainerEntityManagerFactoryBean result = new LocalContainerEntityManagerFactoryBean();
        if(packagesToScan != null) {
            //handle multiple packages separated by comma
            result.setPackagesToScan(packagesToScan.split("\\s*,\\s*"));
        }
        result.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        result.setJpaPropertyMap(hibernateProps);

        return result;
    }

以下是 MultiTenantConnectionProvider 实现

public class MultiTenantConnectionProviderImpl extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl {

    private static final long serialVersionUID = 1L;


    public MultiTenantConnectionProviderImpl()
    {
    }

    @Override
    protected DataSource selectAnyDataSource()
    {
        return null;
    }

    @Override
    protected DataSource selectDataSource(String tenantIdentifier) {
        //This in turn makes a call to a remote service to fetch current tenant 
        //database details and then creates a datasource for it.
        return DataSourceManager.getDataSource(tenantIdentifier);
    }

    @Override
    public Connection getAnyConnection() throws SQLException{
        //Called during server startup. Cannot do any thing  as tenant is not known
        // and there is no default datasource
        throw new SQLException("not implemented in multi-tenant environment");
    }

    @Override
    public void releaseAnyConnection(Connection connection) throws SQLException
    {
        //Called during server startup. Cannot do any thing  as tenant is not known
        // and there is no default datasource
        throw new SQLException("not implemented in multi-tenant environment");
    }
}

标签: hibernatespring-bootmulti-tenant

解决方案


这仅在启动期间调用,为什么不为启动使用默认的tenantIdentifier?

更新:

所以如果这是不可能的。为什么不使用 DummyConnection 类实现 Connection 接口并在启动时返回该接口?


推荐阅读