Ada*_*lle 5 java hibernate multi-tenant spring-data-jpa
我有一个系统,其中有一个未知数量的租户(同一数据库服务器上的不同数据库实例).我有一个工作代码,用户登录并选择了正确的租户,我可以读取该租户的配置表.
我希望应用程序在开始时循环遍历所有租户,阅读配置并对其进行操作.在迁移到Spring Data JPA(由hibernate支持)之前,这很简单,因为我分别连接到每个数据库实例.
我不认为我可以使用Spring @Transactional,因为它只设置一个连接.
我希望使用与同一个bean相同的存储库接口,因为当我只需要一次命中一个租户时,这可以工作.
我确实有一个class MultiTenantConnectionProviderImpl extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl给我一个给定租户的dataSource,但我不确定如何在@Service类的方法中使用它?
我不确定是否应该删除我之前的答案,编辑它或者什么。因此,如果 MOD 可以让我知道正确的程序,我会很乐意遵守。
@Transactional事实证明我关于不去工作的用法是正确的。我最终使用 和 的自定义实现AbstractRoutingDataSource来替换我的MultiTenantConnectionProviderImpl和CurrentTenantResolverImpl。我使用这个新的数据源而不是设置hibernate.multiTenancy hibernate.multi_tenant_connection_provider和hibernate.tenant_identifier_resolver
我的临时覆盖类如下所示:
public class MultitenancyTemporaryOverride implements AutoCloseable
{
static final ThreadLocal<String> tenantOverride = new NamedThreadLocal<>("temporaryTenantOverride");
public void setCurrentTenant(String tenantId)
{
tenantOverride.set(tenantId);
}
public String getCurrentTenant()
{
return tenantOverride.get();
}
@Override
public void close() throws Exception
{
tenantOverride.remove();
}
}
Run Code Online (Sandbox Code Playgroud)
我的 TenantRoutingDataSource 如下所示:
@Component
public class TenantRoutingDataSource extends AbstractDataSource implements InitializingBean
{
@Override
public Connection getConnection() throws SQLException
{
return determineTargetDataSource().getConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException
{
return determineTargetDataSource().getConnection(username, password);
}
@Override
public void afterPropertiesSet() throws Exception
{
}
protected String determineCurrentLookupKey()
{
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String database = "shared";
if (authentication != null && authentication.getPrincipal() instanceof MyUser)
{
MyUser user = (MyUser) authentication.getPrincipal();
database = user.getTenantId();
}
String temporaryOverride = MultitenancyTemporaryOverride.tenantOverride.get();
if (temporaryOverride != null)
{
database = temporaryOverride;
}
return database;
}
protected DataSource determineTargetDataSource()
{
return selectDataSource(determineCurrentLookupKey());
}
public DataSource selectDataSource(String tenantIdentifier)
{
//I use C3P0 for my connection pool
PooledDataSource pds = C3P0Registry.pooledDataSourceByName(tenantIdentifier);
if (pds == null)
pds = getComboPooledDataSource(tenantIdentifier);
return pds;
}
private ComboPooledDataSource getComboPooledDataSource(String tenantIdentifier)
{
ComboPooledDataSource cpds = new ComboPooledDataSource(tenantIdentifier);
cpds.setJdbcUrl("A JDBC STRING HERE");
cpds.setUser("MyDbUsername");
cpds.setPassword("MyDbPassword");
cpds.setInitialPoolSize(10);
cpds.setMaxConnectionAge(10000);
try
{
cpds.setDriverClass("com.informix.jdbc.IfxDriver");
}
catch (PropertyVetoException e)
{
throw new RuntimeException("Weird error when setting the driver class", e);
}
return cpds;
}
}
Run Code Online (Sandbox Code Playgroud)
然后,我在创建实体管理器工厂 bean 时将其提供给我的自定义数据源。
@Service
public class TestService
{
public void doSomeGets()
{
List<String> tenants = getListSomehow();
try(MultitenancyTemporaryOverride tempOverride = new MultitenancyTemporaryOverride())
{
for(String tenant : tenants)
{
tempOverride.setCurrentTenant(tenant);
//do some work here, which only applies to the tenant
}
}
catch (Exception e)
{
logger.error(e);
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
927 次 |
| 最近记录: |