1前書き


マルチテナンシー

は、複数のクライアントまたはテナントが単一のリソースを使用すること、またはこの記事の文脈では単一のリソースを使用することを許可します。データベースインスタンス。目的は、各テナントが必要とする情報を共有データベースから分離することです。

このチュートリアルでは、Hibernate 5でマルチテナンシーを設定するためのさまざまな方法を紹介します。

** 2 Mavenの依存関係+

**

<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>5.2.12.Final</version>
</dependency>

テストにはH2インメモリデータベースを使用しますので、https://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22com.h2database%22%20AND%20aも追加しましょう。 %3A%22h2%22

pom.xml

ファイルへの[この依存関係]:

<dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <version>1.4.196</version>
</dependency>


3 Hibernate

のマルチテナンシーを理解する

公式のhttps://docs.jboss.org/hibernate/orm/5.2/userguide/html

single/Hibernate

User__Guide.html#multitenacy[Hibernate User Guide]で述べられているように、Hibernateにおけるマルチテナンシーへのアプローチは3つあります。

  • __別のスキーマ – 同じ物理内のテナントごとに1つのスキーマ

データベースインスタンス
**

個別データベース –

1つの個別の物理データベースインスタンス

テナント
**

Partitioned(Discriminator)Data –

各テナントのデータは

弁別子値で分割

  • 分割(弁別)データアプローチはHibernateではまだサポートされていません** 将来の進歩についてはhttps://hibernate.atlassian.net/browse/HHH-6054[このJIRAの問題]をフォローアップしてください。

いつものように、Hibernateは各アプローチの実装に関する複雑さを抽象化しています。

必要なのは、これら2つのインタフェースの実装を提供することだけです。

データベースとスキーマのアプローチの例を説明する前に、各概念の詳細を見てみましょう。


3.1.



MultiTenantConnectionProvider


基本的に、このインタフェースは具体的なテナント識別子にデータベース接続を提供します。

その2つの主な方法を見てみましょう。

interface MultiTenantConnectionProvider extends Service, Wrapped {
    Connection getAnyConnection() throws SQLException;

    Connection getConnection(String tenantIdentifier) throws SQLException;
    //...
}

Hibernateが使用するテナント識別子を解決できない場合、接続を取得するためにメソッド

getAnyConnection

が使用されます。それ以外の場合は、メソッド

getConnection

を使用します。

  • Hibernateはデータベース接続をどのように定義するかに応じてこのインターフェースの2つの実装を提供します。

  • を使う


DataSource

Javaからのインタフェース – 私たちは

DataSourceBasedMultiTenantConnectionProviderImpl

実装
** Hibernateの

ConnectionProvider

インターフェースを使用する – 使用します


AbstractMultiTenantConnectionProvider

実装


3.2.



CurrentTenantIdentifierResolver


テナント識別子を解決する方法はたくさんあります。例えば、私たちの実装は設定ファイルで定義された1つのテナント識別子を使うことができます。

もう1つの方法は、パスパラメータからテナント識別子を使用することです。

このインターフェースを見てみましょう:

public interface CurrentTenantIdentifierResolver {

    String resolveCurrentTenantIdentifier();

    boolean validateExistingCurrentSessions();
}

Hibernateはメソッド

resolveCurrentTenantIdentifier

を呼び出してテナント識別子を取得します。 Hibernateにすべての既存のセッションを同じテナント識別子に属することを検証させたい場合、メソッド

validateExistingCurrentSessions

はtrueを返すべきです。


4スキーマアプローチ

この戦略では、同じ物理データベースインスタンス内で異なるスキーマまたはユーザーを使用します。このアプローチは、アプリケーションで最高のパフォーマンスが必要で、テナントごとのバックアップなどの特別なデータベース機能を犠牲にする可能性がある場合に使用する必要があります。

また、テスト中に、

CurrentTenantIdentifierResolver

インターフェースを選択して、1つのテナント識別子を指定します。

public abstract class MultitenancyIntegrationTest {

    @Mock
    private CurrentTenantIdentifierResolver currentTenantIdentifierResolver;

    private SessionFactory sessionFactory;

    @Before
    public void setup() throws IOException {
        MockitoAnnotations.initMocks(this);

        when(currentTenantIdentifierResolver.validateExistingCurrentSessions())
          .thenReturn(false);

        Properties properties = getHibernateProperties();
        properties.put(
          AvailableSettings.MULTI__TENANT__IDENTIFIER__RESOLVER,
          currentTenantIdentifierResolver);

        sessionFactory = buildSessionFactory(properties);

        initTenant(TenantIdNames.MYDB1);
        initTenant(TenantIdNames.MYDB2);
    }

    protected void initTenant(String tenantId) {
        when(currentTenantIdentifierResolver
         .resolveCurrentTenantIdentifier())
           .thenReturn(tenantId);
        createCarTable();
    }
}


MultiTenantConnectionProvider

インターフェースの実装は、接続が要求されるたびに使用するスキーマを設定します。

class SchemaMultiTenantConnectionProvider
  extends AbstractMultiTenantConnectionProvider {

    private ConnectionProvider connectionProvider;

    public SchemaMultiTenantConnectionProvider() throws IOException {
        this.connectionProvider = initConnectionProvider();
    }

    @Override
    protected ConnectionProvider getAnyConnectionProvider() {
        return connectionProvider;
    }

    @Override
    protected ConnectionProvider selectConnectionProvider(
      String tenantIdentifier) {

        return connectionProvider;
    }

    @Override
    public Connection getConnection(String tenantIdentifier)
      throws SQLException {

        Connection connection = super.getConnection(tenantIdentifier);
        connection.createStatement()
          .execute(String.format("SET SCHEMA %s;", tenantIdentifier));
        return connection;
    }

    private ConnectionProvider initConnectionProvider() throws IOException {
        Properties properties = new Properties();
        properties.load(getClass()
          .getResourceAsStream("/hibernate.properties"));

        DriverManagerConnectionProviderImpl connectionProvider
          = new DriverManagerConnectionProviderImpl();
        connectionProvider.configure(properties);
        return connectionProvider;
    }
}

そのため、各テナントごとに1つずつ、合計2つのスキーマを持つ1つのインメモリH2データベースを使用します。

スキーママルチテナンシーモードと

MultiTenantConnectionProvider

インターフェースの実装を使用するように

hibernate.properties

を設定しましょう


_:

_

hibernate.connection.url=jdbc:h2:mem:mydb1;DB__CLOSE__DELAY=-1;\
  INIT=CREATE SCHEMA IF NOT EXISTS MYDB1\\;CREATE SCHEMA IF NOT EXISTS MYDB2\\;
hibernate.multiTenancy=SCHEMA
hibernate.multi__tenant__connection__provider=\
  com.baeldung.hibernate.multitenancy.schema.SchemaMultiTenantConnectionProvider

テストの目的で、2つのスキーマを作成するように

hibernate.connection.url

プロパティを設定しました。スキーマはすでに配置されているはずなので、これは実際のアプリケーションには必要ありません。

このテストでは、テナント

myDb1に

Car

エントリを1つ追加します。このエントリがデータベースに格納されていて、テナント

myDb2にはないことを確認します。

@Test
void whenAddingEntries__thenOnlyAddedToConcreteDatabase() {
    whenCurrentTenantIs(TenantIdNames.MYDB1);
    whenAddCar("myCar");
    thenCarFound("myCar");
    whenCurrentTenantIs(TenantIdNames.MYDB2);
    thenCarNotFound("myCar");
}

テストでわかるように、

whenCurrentTenantIs

メソッドを呼び出すときにテナントを変更します。


5データベースアプローチ

データベースマルチテナンシーアプローチでは、テナントごとに

異なる物理データベースインスタンスを使用します

。各テナントは完全に分離されているため、** テナントごとのバックアップなどの特別なデータベース機能が必要な場合は、最高のパフォーマンスを得るためにこの戦略を選択する必要があります。

データベースアプローチでは、上記と同じ

MultitenancyIntegrationTest

クラスと

CurrentTenantIdentifierResolver

インターフェイスを使用します。


MultiTenantConnectionProvider

インターフェースでは、テナントごとの

ConnectionProvider

を取得するために

Map

コレクションを使用します。

class MapMultiTenantConnectionProvider
  extends AbstractMultiTenantConnectionProvider {

    private Map<String, ConnectionProvider> connectionProviderMap
     = new HashMap<>();

    public MapMultiTenantConnectionProvider() throws IOException {
        initConnectionProviderForTenant(TenantIdNames.MYDB1);
        initConnectionProviderForTenant(TenantIdNames.MYDB2);
    }

    @Override
    protected ConnectionProvider getAnyConnectionProvider() {
        return connectionProviderMap.values()
          .iterator()
          .next();
    }

    @Override
    protected ConnectionProvider selectConnectionProvider(
      String tenantIdentifier) {

        return connectionProviderMap.get(tenantIdentifier);
    }

    private void initConnectionProviderForTenant(String tenantId)
     throws IOException {
        Properties properties = new Properties();
        properties.load(getClass().getResourceAsStream(
          String.format("/hibernate-database-%s.properties", tenantId)));
        DriverManagerConnectionProviderImpl connectionProvider
          = new DriverManagerConnectionProviderImpl();
        connectionProvider.configure(properties);
        this.connectionProviderMap.put(tenantId, connectionProvider);
    }
}



ConnectionProvider

には、設定ファイル__hibernate-database- <テナント識別子> .propertiesを介して入力されます。これには、すべての接続詳細が含まれます。

hibernate.connection.driver__class=org.h2.Driver
hibernate.connection.url=jdbc:h2:mem:<Tenant Identifier>;DB__CLOSE__DELAY=-1
hibernate.connection.username=sa
hibernate.dialect=org.hibernate.dialect.H2Dialect

最後に、データベースのマルチテナンシーモードと

MultiTenantConnectionProvider

インターフェイスの実装を使用するように

hibernate.properties

を更新します。

hibernate.multiTenancy=DATABASE
hibernate.multi__tenant__connection__provider=\
  com.baeldung.hibernate.multitenancy.database.MapMultiTenantConnectionProvider

スキーマアプローチとまったく同じテストを実行すると、テストは再びパスします。


6. 結論

この記事では、個別のデータベースと個別のスキーマアプローチを使用したマルチテナンシーのHibernate 5サポートについて説明します。これら2つの戦略の違いを検証するために、非常に単純化された実装と例を提供します。

この記事で使用されている完全なコードサンプルは、https://github.com/eugenp/tutorials/tree/master/persistence-modules/hibernate5[Githubプロジェクト]から入手できます。