我对使用hibernate作为提供程序和SQLServer作为数据库实现多租户解决方案很感兴趣。 我使用的是基于模式的方法,意味着一个数据库和不同的模式。
具体来说,我的问题是如何使用SQLServer更改执行模式。我在MySQL use $database
中使用过这种方法,
PostgreSQL SET search_path TO $schema
和Oracle ALTER SESSION SET CURRENT_SCHEMA = $schema
和我能够成功地从一个模式切换到另一个模式,注意到模式概念因数据库而异。
然而,使用SQLServer,我知道没有改变执行模式的事情。我知道在未授予用户sysadmin角色的情况下切换给定用户的默认架构的可能性。我也知道使用folllwing查询的模拟概念:EXECUTE AS USER = $user
。模拟允许一个用户模仿其他用户,这样就可以更改当前用户。
切换当前用户允许按定义切换执行模式,因为一旦用户被更改,执行模式就是新用户的默认模式。然而,模仿有一定的局限性,因为我们无法进行32次模拟。虽然每次模仿后都可以执行revert
以避免达到极限模拟次数,但此解决方案并不适用于我的情况和我正在寻找替代方案。
有没有人对使用SQLServer和基于模式的方法的多租户实现有任何建议?
除了我提到的切换执行模式之外,还有其他解决方案。
非常感谢你的帮助。非常感谢你。
答案 0 :(得分:2)
以下是需要考虑的要点
如果由于大量数据增长而计划从共享数据库中扩展出特定租户,以便提供更高的响应时间,那么上述在商店中持久保存租户连接字符串的选项将会很有帮助。其他租户的表现更佳。
Azure Shard Map上的修改强>
实际上,当您将用户映射到SQL Server中的架构时,您不需要在查询中使用[Schema].[Table]
,而是可以直接使用[Table]
,访问自动发生。实际上,您可以使用类似ALTER USER erpadmin WITH DEFAULT_SCHEMA = erpadmin;
的方式将用户映射到架构,查询不需要表的架构前缀。更多详情here
HTH
答案 1 :(得分:0)
我也有这个问题,我的解决方案是在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;
}
};
}