Spring启动+Hibernate+Oracle模式多租户

Spring启动+Hibernate+Oracle模式多租户,spring,oracle,spring-boot,multi-tenant,hikaricp,Spring,Oracle,Spring Boot,Multi Tenant,Hikaricp,我正在尝试一个基于模式的多租户解决方案,类似于Oracle而不是Postgres 例如,我有三个模式:FOO、BAR和BAZ。BAR和BAZ都有一个名为MESSAGES的表。FOO已被授予在BAR.MESSAGES和BAZ.MESSAGES上选择的权限。所以如果我以FOO的形式连接,然后执行 SELECT * FROM BAR.MESSAGES; 然后我得到了预期的结果。但是如果我省略了模式名称,例如从消息中选择*,那么我会得到ORA-00942:表或视图不存在连接使用了错误的模式 这是我的D

我正在尝试一个基于模式的多租户解决方案,类似于Oracle而不是Postgres

例如,我有三个模式:FOO、BAR和BAZ。BAR和BAZ都有一个名为MESSAGES的表。FOO已被授予在BAR.MESSAGES和BAZ.MESSAGES上选择的权限。所以如果我以FOO的形式连接,然后执行

SELECT * FROM BAR.MESSAGES;
然后我得到了预期的结果。但是如果我省略了模式名称,例如从消息中选择*,那么我会得到ORA-00942:表或视图不存在连接使用了错误的模式

这是我的Dao/存储库:

@Repository
public interface MessageDao extends CrudRepository<Foo, Long> {
}
关于为什么底层连接没有按预期切换模式,有什么想法吗

更新1

也许这与底层的Oracle连接有关。有。文件说:

用户还可以选择将当前用户值附加到 ADT name通过将此属性设置为获取完全限定名 符合事实的请注意,获取数据需要网络往返 当前_架构值

但是,即使我将其设置为true,问题仍然存在——Doracle.jdbc.createDescriptorUseCurrentSchemaForSchemaName=true。这可能是因为连接上的用户名仍然是FOO,即使在更改session以将模式设置为BAR之后,连接上的schema仍然是BAR。但这意味着OracleConnection文档是不正确的,不是吗

更新2 我没有包括我们在这里也使用Spring数据JPA这一事实。也许这和问题有关? 我发现,如果我在实体中包含硬编码的模式名称,那么它就可以工作,例如@Tableschema=BAR,但是如果有硬编码的值,就没有可接受的解决方案


如果我们将查询重写为native@Query,然后在SQL中包含{h-schema},那么它也可能会起作用,但在Hibernate中,这是默认模式,而不是“当前”动态模式,因此,这也不完全正确。

事实证明,在控制器的第一行设置当前租户(如TenantContext.setCurrentTenantBAR)为时已晚,Spring已经创建了一个事务?。我将实现更改为使用servlet过滤器将租户id从标头设置为请求属性,然后在TenantIdentifierResolver中获取该属性,而不是使用TenantContext。现在它正常工作了,没有我在更新中提到的任何东西

@GetMapping("/findAll")
public List<Message> findAll() {
    TenantContext.setCurrentTenant("BAR");
    var result = messageDao.findAll();
    return result;
}
@Configuration
public class MessageConfig {
    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        return new HibernateJpaVendorAdapter();
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
                                                                       MultiTenantConnectionProvider multiTenantConnectionProvider,
                                                                       CurrentTenantIdentifierResolver tenantIdentifierResolver) {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource);
        em.setPackagesToScan(Message.class.getPackageName());

        em.setJpaVendorAdapter(this.jpaVendorAdapter());

        Map<String, Object> jpaProperties = new HashMap<>();
        jpaProperties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
        jpaProperties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProvider);
        jpaProperties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantIdentifierResolver);
        jpaProperties.put(Environment.FORMAT_SQL, true);

        em.setJpaPropertyMap(jpaProperties);
        return em;
    }
@Component
public class MultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider {

    @Autowired
    private DataSource dataSource;

    @Override
    public Connection getAnyConnection() throws SQLException {
        return dataSource.getConnection();
    }

    @Override
    public void releaseAnyConnection(Connection connection) throws SQLException {
        connection.close();
    }

    @Override
    public Connection getConnection(String currentTenantIdentifier) throws SQLException {
        String tenantIdentifier = TenantContext.getCurrentTenant();
        final Connection connection = getAnyConnection();
        try (Statement statement = connection.createStatement()) {
            statement.execute("ALTER SESSION SET CURRENT_SCHEMA = BAR");
        } catch (SQLException e) {
            throw new HibernateException("Problem setting schema to " + tenantIdentifier, e);
        }
        return connection;
    }

    @Override
    public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
        try (Statement statement = connection.createStatement()) {
            statement.execute("ALTER SESSION SET CURRENT_SCHEMA = FOO");
        } catch (SQLException e) {
            throw new HibernateException("Problem setting schema to " + tenantIdentifier, e);
        }
        connection.close();
    }

    @SuppressWarnings("rawtypes")
    @Override
    public boolean isUnwrappableAs(Class unwrapType) {
        return false;
    }

    @Override
    public <T> T unwrap(Class<T> unwrapType) {
        return null;
    }

    @Override
    public boolean supportsAggressiveRelease() {
        return true;
    }
}
@Component
public class TenantIdentifierResolver implements CurrentTenantIdentifierResolver {

    @Override
    public String resolveCurrentTenantIdentifier() {
        String tenantId = TenantContext.getCurrentTenant();
        if (tenantId != null) {
            return tenantId;
        }
        return "BAR";
    }

    @Override
    public boolean validateExistingCurrentSessions() {
        return true;
    }
}