Útmutató a hibás állapotú multitenanciához 5

1. Bemutatkozás

A Multitenancy lehetővé teszi, hogy több ügyfél vagy bérlő használjon egyetlen erőforrást, vagy a cikk kapcsán egyetlen adatbázis-példányt. A cél az az egyes bérlők számára szükséges információk elkülönítése a megosztott adatbázistól.

Ebben az oktatóanyagban különböző megközelítéseket mutatunk be a multitenancy beállításához a Hibernate 5-ben.

2. Maven-függőségek

Be kell foglalnunk a hibernált mag függőség a pom.xml fájl:

 org.hibernate hibernate-core 5.2.12.Végső 

A teszteléshez H2 memóriában lévő adatbázist fogunk használni, ezért adjuk hozzá ezt a függőséget is a pom.xml fájl:

 com.h2adatbázis h2 1.4.196 

3. A multitenancia megértése hibernált állapotban

Amint a hivatalos hibernált felhasználói útmutatóban említettük, a hibernált állapotban háromféle megközelítés létezik:

  • Külön séma - bérlőnként egy séma ugyanabban a fizikai adatbázis-példányban
  • Külön adatbázis - bérlőnként egy külön fizikai adatbázis-példány
  • Particionált (diszkriminátor) adatok - az egyes bérlők adatait egy diszkriminátor érték osztja fel

A A Hibernate még nem támogatja a particionált (diszkriminátor) adat megközelítést. A jövőbeli előrehaladás érdekében kövesse nyomon ezt a JIRA-kérdést.

Szokás szerint a Hibernate elvonatkoztatja az egyes megközelítések megvalósításának bonyolultságát.

Csak arra van szükségünk biztosítja ennek a két interfésznek a megvalósítását:

  • MultiTenantConnectionProvider - bérlőnként biztosítja a kapcsolatokat

  • CurrentTenantIdentifierResolver - feloldja a bérlő azonosítóját használni

Lássuk részletesebben az egyes fogalmakat, mielőtt végignéznénk az adatbázis és a séma megközelítési példákat.

3.1.MultiTenantConnectionProvider

Alapvetően ez a felület adatbázis-kapcsolatot biztosít egy konkrét bérlő-azonosító számára.

Nézzük meg két fő módszerét:

interfész A MultiTenantConnectionProvider kiterjeszti a szolgáltatást, a Wrapped {Connection getAnyConnection () dobja az SQLException-t; Connection getConnection (String tenantIdentifier) ​​dobja az SQLException-t; // ...}

Ha a hibernálás nem tudja megoldani a bérlő azonosítóját, akkor a módszert használja getAnyConnection hogy kapcsolatot szerezzek. Ellenkező esetben a módszert fogja használni getConnection.

A hibernálás ennek a felületnek két megvalósítását biztosítja attól függően, hogy hogyan definiáljuk az adatbázis-kapcsolatokat:

  • A Java-ból származó DataSource felület használata - a DataSourceBasedMultiTenantConnectionProviderImpl végrehajtás
  • Használni a ConnectionProvider interfész Hibernate-től - a AbstractMultiTenantConnectionProvider végrehajtás

3.2.CurrentTenantIdentifierResolver

Vannak a bérlő azonosítójának feloldásának számos lehetséges módja. Például megvalósításunk egy konfigurációs fájlban definiált bérlői azonosítót használhat.

Egy másik módszer lehet a bérlő azonosítójának használata egy útparaméterből.

Lássuk ezt a felületet:

nyilvános felület CurrentTenantIdentifierResolver {String ResolCurrentTenantIdentifier (); logikai validateExistingCurrentSessions (); }

Hibernate hívja a módszert resolCurrentTenantIdentifier hogy megkapja a bérlő azonosítóját. Ha azt akarjuk, hogy a Hibernate validálja az összes létező munkamenetet, ugyanahhoz a bérlő azonosítóhoz tartozik, a módszer validateExistingCurrentSessions vissza kell térnie igaznak.

4. Séma megközelítés

Ebben a stratégiában különböző sémákat vagy felhasználókat fogunk használni ugyanabban a fizikai adatbázis-példányban. Ezt a megközelítést akkor kell alkalmazni, ha a legjobb teljesítményre van szükségünk az alkalmazásunkhoz, és feláldozhatja a speciális adatbázis-szolgáltatásokat, például a bérlőnkénti biztonsági mentést.

Ezenkívül megcsúfoljuk a CurrentTenantIdentifierResolver interfész egy bérlő azonosító megadásához, amelyet a teszt során választottunk:

public abstract class MultitenancyIntegrationTest {@Mock private CurrentTenantIdentifierResolver currentTenantIdentifierResolver; privát SessionFactory sessionFactory; @A nyilvános void beállítása előtt () az IOException dobja {MockitoAnnotations.initMocks (this); mikor (currentTenantIdentifierResolver.validateExistingCurrentSessions ()) .thenReturn (hamis); Tulajdonságok tulajdonságai = getHibernateProperties (); tulajdonságok.put (AvailableSettings.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolver); sessionFactory = buildSessionFactory (tulajdonságok); initTenant (TenantIdNames.MYDB1); initTenant (TenantIdNames.MYDB2); } protected void initTenant (String tenantId) {mikor (currentTenantIdentifierResolver .resolveCurrentTenantIdentifier ()) .thenReturn (tenantId); createCarTable (); }}

A MultiTenantConnectionProvider interfész lesz állítsa be a sémát arra, hogy minden alkalommal használja a kapcsolatot:

class SchemaMultiTenantConnectionProvider kiterjeszti az AbstractMultiTenantConnectionProvider {private ConnectionProvider connectionProvider; public SchemaMultiTenantConnectionProvider () dobja az IOException-t {this.connectionProvider = initConnectionProvider (); } @Orride védett ConnectionProvider getAnyConnectionProvider () {return connectionProvider; } @Orride védett ConnectionProvider selectConnectionProvider (String tenantIdentifier) ​​{return connectionProvider; } @Orride public Connection getConnection (String tenantIdentifier) ​​dobja az SQLException {Connection connection = super.getConnection (tenantIdentifier); connection.createStatement () .execute (String.format ("SET SCHEMA% s;", tenantIdentifier)); visszatérő kapcsolat; } private ConnectionProvider initConnectionProvider () dobja az IOException {Properties tulajdonságok = new Properties (); tulajdonságok.load (getClass () .getResourceAsStream ("/ hibernate.properties")); DriverManagerConnectionProviderImpl connectionProvider = új DriverManagerConnectionProviderImpl (); connectionProvider.configure (tulajdonságok); return connectionProvider; }}

Tehát egy memóriában lévő H2 adatbázist fogunk használni, két sémával - bérlőnként egyet.

Konfiguráljuk a hibernálni.tulajdonságok a séma multitenancy mód használatához és a MultiTenantConnectionProvider felület:

hibernate.connection.url = jdbc: h2: mem: mydb1; DB_CLOSE_DELAY = -1; \ INIT = Séma létrehozása, ha nem létezik MYDB1 \; Séma létrehozása, ha nem létezik MYDB2 \; hibernate.multiTenancy = SCHEMA hibernate.multi_tenant_connection_provider = \ com.baeldung.hibernate.multitenancy.schema.SchemaMultiTenantConnectionProvider

Tesztünk céljából konfiguráltuk a hibernálni.kapcsolat.url tulajdonság két séma létrehozásához. Erre nincs szükség egy valódi alkalmazáshoz, mivel a sémáknak már meg kell lenniük.

Tesztünkhöz hozzáadunk egyet Autó bejegyzés a bérlőbe myDb1. Ellenőrizzük, hogy ezt a bejegyzést tárolták-e az adatbázisunkban, és hogy nincs-e a bérlőnél myDb2:

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

Mint a teszten láthatjuk, a bérlőt megváltoztatjuk, amikor felhívjuk a whenCurrentTenantIs módszer.

5. Adatbázis megközelítés

Az Adatbázis több bérleti megközelítés használja bérlőnként különböző fizikai adatbázis-példányok. Mivel minden bérlő teljesen elszigetelődött, akkor kell ezt a stratégiát választanunk, ha speciális adatbázis-funkciókra van szükségünk, például bérlőnkénti biztonsági mentésre, mint amennyire a legjobb teljesítményre van szükségünk.

Az adatbázis megközelítésnél ugyanezt fogjuk használni MultitenancyIntegrationTest osztály és a CurrentTenantIdentifierResolver felület, mint fent.

A MultiTenantConnectionProvider felületen használjuk a Térkép gyűjtemény, hogy a ConnectionProvider bérlői azonosítónként:

class MapMultiTenantConnectionProvider kiterjeszti az AbstractMultiTenantConnectionProvider {privát Map connectionProviderMap = új HashMap (); public MapMultiTenantConnectionProvider () dobja az IOException {initConnectionProviderForTenant (TenantIdNames.MYDB1); initConnectionProviderForTenant (TenantIdNames.MYDB2); } @Orride védett ConnectionProvider getAnyConnectionProvider () {return connectionProviderMap.values ​​() .iterator () .next (); } @Orride védett ConnectionProvider selectConnectionProvider (String tenantIdentifier) ​​{return connectionProviderMap.get (tenantIdentifier); } private void initConnectionProviderForTenant (String tenantId) dobja az IOException {Properties properties = new Properties (); tulajdonságok.load (getClass (). getResourceAsStream (String.format ("/ hibernate-database-% s.properties", tenantId)); DriverManagerConnectionProviderImpl connectionProvider = új DriverManagerConnectionProviderImpl (); connectionProvider.configure (tulajdonságok); this.connectionProviderMap.put (tenantId, connectionProvider); }}

Minden egyes ConnectionProvider a konfigurációs fájlon keresztül kerül feltöltésre hibernate-database-.properties, amely az összes csatlakozási adattal rendelkezik:

hibernate.connection.driver_class = org.h2.Driver hibernate.connection.url = jdbc: h2: mem:; DB_CLOSE_DELAY = -1 hibernate.connection.username = sa hibernate.dialect = org.hibernate.dialect.H2Dialect

Végül frissítsük a hibernálni.tulajdonságok ismét használhatja az adatbázis multitenancy módot és a MultiTenantConnectionProvider felület:

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

Ha pontosan ugyanazt a tesztet futtatjuk, mint a séma megközelítésnél, akkor a teszt újra sikeres lesz.

6. Következtetés

Ez a cikk a Hibernate 5 támogatását tárgyalja a különféle adatbázisok és a különálló séma megközelítések használatával. Nagyon leegyszerűsített megvalósításokat és példákat kínálunk a két stratégia közötti különbségek feltárására.

A cikkben használt teljes kódminták elérhetők a GitHub projektünkön.