【问题标题】:Schema based multitenancy with SQLServer and Hibernate使用 SQLServer 和 Hibernate 的基于模式的多租户
【发布时间】:2018-02-20 17:41:43
【问题描述】:

我对使用 hibernate 作为提供程序和 SQLServer 作为数据库来实现多租户解决方案很感兴趣。 我正在使用基于模式的方法,这意味着一个数据库和不同的模式。

具体来说,我的问题是如何使用 SQLServer 更改执行模式。我在 MySQL use $database 中使用了这种方法, PostgreSQL SET search_path TO $schema 和 Oracle ALTER SESSION SET CURRENT_SCHEMA = $schema 我能够成功地从一种模式切换到另一种模式,并注意到模式概念因数据库而异。

但是,对于 SQLServer,我知道不存在更改执行架构这样的事情。我知道在未授予用户 sysadmin 角色的情况下切换给定用户的默认模式的可能性。我也知道使用以下查询的模拟概念:EXECUTE AS USER = $user。模拟允许一个用户模拟另一个用户,这提供了更改当前用户的可能性。

切换当前用户允许根据定义切换执行架构,因为一旦更改用户,执行架构就是新用户的默认架构。但是,模拟有一个限制,因为我们不能执行超过 32 次的模拟。虽然可以在每次模拟后执行revert 以避免达到限制模拟次数,但这种解决方案不适合我的情况,我'我正在寻找替代方案。

是否有人对使用 SQLServer 和基于模式的方法实现多租户有任何建议?

除了我提到的切换执行模式之外,还有其他解决方案吗?

非常感谢您的帮助。非常感谢。

【问题讨论】:

  • 嗨..有什么解决办法吗?

标签: sql-server hibernate jpa database-schema multi-tenant


【解决方案1】:

以下是考虑的要点

  1. SQL Server 通过 [dbo].[Users] 和 [Tenant01].[Users] 等表中的前缀来区分架构
  2. 由于您没有提到解决 SQL Server 中每个架构的数据库连接的方式,我建议您可以查看支持您的用例的 Azure Shard Map 或构建您自己的在这种情况下,您将根据建立的租户上下文从集中存储中获取连接字符串。例如:Tenant01 将映射到 ConnectionString C01,它具有相同的数据库但具有不同的用户 ID 和密码。这种方法是更普遍遵循的方法。不过,您也可以选择 Azure Shard Map,它会在幕后为您进行映射。
  3. 在这种情况下,当您预配架构时,您还会创建 SQL 用户或将 Azure AD 用户映射到架构并授予架构上的必要权限。这样可以确保访问正常。

上述将租户连接字符串持久保存在商店中的选项将很有帮助,如果您计划由于大量数据增长而从共享数据库中横向扩展特定租户,以便提供更长的响应时间,并且还其他租户的性能更好。

More 在 Azure 分片地图上

编辑

实际上,当您在 SQL Server 中将用户映射到架构时,您不需要在 Query 中使用[Schema].[Table],而是可以直接使用[Table],访问会自动发生。从那时起,您实际上可以使用 likeALTER USER erpadmin WITH DEFAULT_SCHEMA = erpadmin; 将用户映射到架构,查询不需要表的架构前缀。更多详情here

HTH

【讨论】:

  • 嗨 Saravanan,非常感谢您的建议。我知道表名选项中的前缀,但这不是我需要的。另外我不是在寻找一种从头开始实现多租户的方法,所以 Azure Shard Map 不适合我。正如我在其他数据库中提到的那样,您对使用 SQLServer 实际切换执行模式有什么建议吗?谢谢。
  • 我已经更新了帖子,请参考并告诉我们是否有帮助!
  • 感谢您的更新。我要求切换执行模式而不是默认模式,因为我已经尝试过ALTER USER myUser WITH DEFAULT_SCHEMA = mySchema; .我没有采用这种替代方案,因为它引起了我的两个担忧:系统管理员角色,尤其是多线程。
【解决方案2】:

我也有这个问题,我的解决方案是在 JPA 配置中添加一个默认架构,例如:

 @Bean()
public LocalContainerEntityManagerFactoryBean entityManager() {
    Map<String, Object> jpaProperties = new HashMap<>();

    jpaProperties.put("exclude-unlisted-classes", true);
    jpaProperties.put(org.hibernate.cfg.Environment.DIALECT, "org.hibernate.dialect.SQLServer2012Dialect");

    jpaProperties.put(org.hibernate.cfg.Environment.DRIVER, env.getProperty("ENTIDADES_PACKAGE"));
    **jpaProperties.put(org.hibernate.cfg.Environment.INTERCEPTOR, this.hibernateInterceptor());**
    **jpaProperties.put(org.hibernate.cfg.Environment.DEFAULT_SCHEMA, "dbo");**

    LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
    lef.setPersistenceUnitName("SECONDARY_DATABASE_PU");
    lef.setDataSource(secondaryDataSource());
    lef.setPersistenceProvider(new HibernatePersistenceProvider());
    lef.setJpaPropertyMap(jpaProperties);
    lef.setPackagesToScan("br.com.application.model");

    return lef;
}

并添加一个 Hibernate 查询拦截器来替换查询时的默认方案字符串。

@Bean
public Interceptor hibernateInterceptor() {
    return new EmptyInterceptor() {
        @Override
        public String onPrepareStatement(String sql) {
            String prepedStatement = super.onPrepareStatement(sql);

            return (TenantContext.getCurrentTenant() != null)
                    ? prepedStatement.replaceAll("dbo.", String.format("%s.", TenantContext.getCurrentTenant()))
                    : prepedStatement;
        }
    };
}

【讨论】:

    猜你喜欢
    • 2020-02-02
    • 2022-10-04
    • 2020-06-26
    • 1970-01-01
    • 2014-04-07
    • 2023-04-01
    • 1970-01-01
    • 2019-06-23
    • 1970-01-01
    相关资源
    最近更新 更多