跨多个租户查询表(相同的表名)

Ada*_*lle 5 java hibernate multi-tenant spring-data-jpa

我有一个系统,其中有一个未知数量的租户(同一数据库服务器上的不同数据库实例).我有一个工作代码,用户登录并选择了正确的租户,我可以读取该租户的配置表.

我希望应用程序在开始时循环遍历所有租户,阅读配置并对其进行操作.在迁移到Spring Data JPA(由hibernate支持)之前,这很简单,因为我分别连接到每个数据库实例.

我不认为我可以使用Spring @Transactional,因为它只设置一个连接.

我希望使用与同一个bean相同的存储库接口,因为当我只需要一次命中一个租户时,这可以工作.

我确实有一个class MultiTenantConnectionProviderImpl extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl给我一个给定租户的dataSource,但我不确定如何在@Service类的方法中使用它?

Ada*_*lle 2

我不确定是否应该删除我之前的答案,编辑它或者什么。因此,如果 MOD 可以让我知道正确的程序,我会很乐意遵守。

@Transactional事实证明我关于不去工作的用法是正确的。我最终使用 和 的自定义实现AbstractRoutingDataSource来替换我的MultiTenantConnectionProviderImplCurrentTenantResolverImpl。我使用这个新的数据源而不是设置hibernate.multiTenancy hibernate.multi_tenant_connection_providerhibernate.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)