2016-04-06 10 views
5

テナント数が不明なシステムがあります(同じデータベースサーバー上の異なるデータベースインスタンス)。ユーザーがログインして正しいテナントが選択されている作業コードがあり、そのテナントの構成テーブルを読み取ることができます。複数のテナント(同じテーブル名)にまたがるクエリテーブル

開始時にすべてのテナントをループし、その設定を読み、それに基づいて処理するアプリケーションを作成します。 Spring Data JPA(休止状態のバックアップ)に移行する前は、各データベースインスタンスに別々に接続していたので、これは簡単でした。

私はSpringの@Transactionalを使用することはできません。単一の接続だけを設定するためです。

同じビーンと同じリポジトリインターフェイスを使用したいのですが、これは一度に1つのテナントにしか当たっていないときに動作します。

class MultiTenantConnectionProviderImpl extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl私は与えられたテナントのdataSourceを取得しますが、@Serviceクラスのメソッドでどのように使用するのか分かりませんか?

+0

「設定を読み込む」とは、各テナントのデータベース内のテーブルを意味しますか?このブログ[post](http://anakiou.blogspot.my/2015/08/multi-tenant-application-with-spring.html)を見てみるとよいでしょう。データソースのリストがある場合は、アプリケーションの起動時にループするのが難しくないと思います。 – Mustafa

+0

「構成表」を例として使用すると、物事を混乱させる可能性があるため、悪い選択であった可能性があります。私は実際には、さまざまな理由ですべてのテナントの間で読む必要があるいくつかのことがあります。あなたが提供するブログ記事は、私が知らないデータソースを知っていることを示唆しています。 –

+0

マルチテナントとは、データベースサーバ上の 'dropbox_db'、' google_drive_db'、 'sky_drive_db'などの複数のデータベースに表示される' users'というテーブルを持つようなものですか? – Saheed

答えて

2

以前の回答を削除する必要があるかどうかわかりません。eそれか何かを言いなさい。 MODが私に適切な手続きを知らせることができれば、私は喜んで遵守するでしょう。

私は、@Transactionalの使用については正しく機能しませんでした。私はMultiTenantConnectionProviderImplCurrentTenantResolverImplの代わりにAbstractRoutingDataSourceのカスタム実装を使用しました。私はこの新しいデータソースを使用する代わりにhibernate.multiTenancyhibernate.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(); 
    } 
} 

マイ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; 
    } 
} 

その後、私はちょうど提供します私のカスタムデータソースを、Entity Managerファクトリ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); 
     } 
    } 
} 
0

私は1つの解決策に近いと思っていますが、私はあまりにもそれに満足していません。私はより良い答えが出てくるのが大好きです。

EDITED:春やHibernateは一度だけ、現在のテナント識別子リゾルバを呼び出すように見えるように、各時間@Transactional方法が

と呼ばれていないことがCurrentTenantIdentifierResolver実装を変更することに、これはかなりの仕事をしません判明(設定されている場合)現在のテナントIDを取得する(実装者がその設定方法を理解するまで)だけでなく、スレッドローカル変数を参照してオーバーライドされているかどうかを調べる必要もあります設定されています。

このアプローチを使用すると、テナントIDを設定することができます。指定したマルチテナントトランザクションマネージャを使用してサービスメソッドを呼び出し、データを取得できます。

私のテストサービス:

@Service 
public class TestService 
{ 
    @Transactional(transactionManager = "sharedTxMgr") 
    public void doSomeGets() 
    { 
     List<String> tenants = getListSomehow(); 
     try(MultitenancyTemporaryOverride tempOverride = new MultitenancyTemporaryOverride()) 
     { 
      for(String tenant : tenants) 
      { 
       tempOverride.setCurrentTenant(tenant); 
       doTenantSpecificWork(); 
      } 
     } 
     catch (Exception e) 
     { 
      logger.error(e); 
     } 
    } 

    @Transactional(transactionManager = "tenantSpecificTxMgr") 
    public void doTenantSpecificWork() 
    { 
     //do some work here, which only applies to the tenant 
    } 
} 
必ず変数を作るためにAutoCloseableを実施し、ThreadLocalの設定ラップ私のクラスは

地元のスレッドを使用しています

public class MultitenancyTemporaryOverride implements AutoCloseable 
{ 
    static final ThreadLocal<String> tenantOverride = new ThreadLocal<>(); 

    public void setCurrentTenant(String tenantId) 
    { 
     tenantOverride.set(tenantId); 
    } 

    public String getCurrentTenant() 
    { 
     return tenantOverride.get(); 
    } 

    @Override 
    public void close() throws Exception 
    { 
     tenantOverride.remove(); 
    } 

} 

マイテナントリゾルバの実装をクリーンアップされる

public class CurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver 
{ 

    @Override 
    public String resolveCurrentTenantIdentifier() 
    { 
     Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 
     logger.debug(ToStringBuilder.reflectionToString(authentication)); 
     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; 
    } 
関連する問題