マルチテナントSpringBootアプリケーションを実装する方法(各ユーザーは独自のデータベースを持っています)

Nov 21 2020

Spring Bootを使用してREST-APIを構築していますが、データを処理するためにマルチテナント構造を実装したいと思います。ユーザーに関するデータ(ユーザー名、パスワード...、およびこのユーザーに指定されているデータベースを示すフィールド)をMain持つUserテーブルを持つ1つのデータベースを呼び出したいと思いますdatabase。ユーザーがサインアップするたびに、それぞれのDBが作成されます(これは、私が困難に直面しているポイントの1つです)。私はさまざまなチュートリアルを読みましたが、それらはすべてファイル内に事前定義さDatasourceれたものがありapplication.propertiesます。各ユーザーのDBは「オンザフライ」で作成されるか、すでに作成されている場合はアクセスされるため、ここでは明らかにそうではありません。

ワークフローは次のようになります(可能な限り単純に説明されています)。

  1. ユーザーのサインアップ
  2. アプリはユーザーエンティティを作成し、それをMainDBに保存して、ユーザーのそれぞれのDBを作成します
  3. アプリは、ユーザーが認証されているかどうかを確認し、認証されている場合は、DBからデータを取得します。

次に、DBが自動的に作成されるときにDBを埋めることに関して多くの質問があります。しかし、まず最初に:)

私のスタック:POSTGRESQL、Spring Boot

前もって感謝します。

回答

3 MilindBarve Nov 21 2020 at 18:16

マルチテナンシーは、次の手順で必要に応じて実現できます。

  1. LocalContainerEntityManagerFactoryBeanを構成する共有データベース用とテナントデータベース用の2つの構成クラスを追加します。このBeanは、LocalContainerEntityManagerFactoryBeanに必要なマルチテナンシープロパティを設定する必要があります。
 Map<String, Object> properties = hibernateProperties.determineHibernateProperties(
        this.properties.getProperties(), new HibernateSettings());


    properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
    properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, this.connectionProvider);
    properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, this.resolver);
    properties.put(Environment.DIALECT, "org.hibernate.dialect.MySQLDialect");

このクラスは、タイプごとに名前付きBeantransactionManagerも実装する必要があります。例えば

 @Bean(name = "tenantTransactionManager")
  public PlatformTransactionManager transactionManager() {
    JpaTransactionManager tm = new JpaTransactionManager();
    tm.setEntityManagerFactory(this.entityManagerFactory().getObject());
    return tm;
  }

  1. インターフェイスCurrentTenantIdentifierResolverとメソッドresolveCurrentTenantIdentifierを実装します。これにより、現在ログインしているユーザーに基づいたテナントのデータベース名が返されます。または、ユーザーがログインしていない場合はデフォルトのデータベース名

  2. 現在のテナント名を記憶するためのスレッドセーフなコンテキストホルダー

  3. エンティティクラスのサービス実装に@Transactionalアノテーションを付け、適切なエンティティマネージャーのBean名を渡します。

@Transactional("tenantTransactionManager") // for tenant database

そして

@Transactional("transactionManager") // for shared database.

  1. 新しいユーザーがサインアップしたときにデータベーススキーマの作成方法を設定します。テナントデータベース名を共有スキーマのユーザーテーブルの列の1つとして維持します。

  2. Spring Securityを使用している場合は、UserDetailsS​​erviceインターフェイスを実装し、メソッドloadUserByUsernameを実装して、ログインするユーザーの追加情報(テナントデータベース名)を含むTenantUserクラスのオブジェクトを返すようにします。

public class TenantUser extends org.springframework.security.core.userdetails.User {
 

  /** The tenand id. */
  private String tenantId;

これらの手順があなたが望むものを達成するのに役立つことを願っています。これらすべてのステップを詳細に説明している利用可能な記事がたくさんあります。私の実装は私のプロジェクトに深く埋め込まれているため、実際の例として共有できる状態ではありません。

それ以上の質問にお答えします

Renis1235 Nov 23 2020 at 13:43

私はここで私の問題の完全な解決策を見つけました:

マルチテナンシー:Spring DataJPAを使用した複数のデータソースの管理

著者@ Cepr0に感謝します。

不足しているのは、その場でDBを作成することだけです。実装が終了したら、ここで回答を更新します。

更新

私は次のコードでデータベースを作成しました。@ MilindBarveによって推奨されました。だからありがとう。

 Class.forName("org.postgresql.Driver");
 Connection con = DriverManager.getConnection("jdbc:postgresql://localhost:5432/","postgres", "password");
 Statement smt = con.createStatement();

 smt.executeUpdate("CREATE DATABASE [name_of_db_here] WITH OWNER DEFAULT");

更新:新しく作成された各DBのスキーマを初期化し、すべてのテーブル作成を含む.sqlファイルを作成し、FlyWayを使用して新しく作成された各DBを初期化します

// INITIALIZE THE DB
            Flyway flyway = Flyway.configure()
                    .dataSource(dataSource)
                    .target(MigrationVersion.LATEST)
                    .load();

            flyway.migrate();