En guide til multitenancy i dvale 5

1. Introduksjon

Multitenancy tillater at flere klienter eller leietakere bruker en enkelt ressurs eller, i sammenheng med denne artikkelen, en enkelt databaseinstans. Hensikten er for å isolere informasjonen hver leietaker trenger fra den delte databasen.

I denne opplæringen vil vi introdusere ulike tilnærminger for å konfigurere multitenancy i dvalemodus 5.

2. Maven-avhengigheter

Vi må inkludere dvale-kjerne avhengighet i pom.xml fil:

 org. dvale-dvalekjerne 5.2.12.Final 

For testing bruker vi en H2-minne-database, så la oss også legge til denne avhengigheten av pom.xml fil:

 com.h2database h2 1.4.196 

3. Forstå multitenancy i dvale

Som nevnt i den offisielle dvalemodusbrukerhåndboken, er det tre tilnærminger til multitenancy i dvalemodus:

  • Separat skjema - ett skjema per leietaker i samme fysiske databaseinstans
  • Separat database - en separat fysisk database forekomst per leietaker
  • Partisjonerte (diskriminerende) data - dataene for hver leietaker er delt av en diskriminerende verdi

De Partisjonert (diskriminator) Datatilnærming støttes foreløpig ikke av dvalemodus. Følg opp dette JIRA-problemet for fremtidig fremgang.

Som vanlig trekker dvalemodus inn kompleksiteten rundt implementeringen av hver tilnærming.

Alt vi trenger er å gi en implementering av disse to grensesnittene:

  • MultiTenantConnectionProvider - gir forbindelser per leietaker

  • CurrentTenantIdentifierResolver - løser leieridentifikatoren som skal brukes

La oss se mer i detalj hvert konsept før vi går gjennom eksempler på databasen og skjemaet.

3.1.MultiTenantConnectionProvider

I utgangspunktet gir dette grensesnittet en databaseforbindelse for en konkret leietakeridentifikator.

La oss se de to viktigste metodene:

grensesnitt MultiTenantConnectionProvider utvider Service, Wrapped {Connection getAnyConnection () kaster SQLException; Connection getConnection (String tenantIdentifier) ​​kaster SQLException; // ...}

Hvis dvalemodus ikke kan løse leietakeridentifikatoren som skal brukes, vil den bruke metoden getAnyConnection for å få en forbindelse. Ellers vil den bruke metoden getConnection.

Hibernate tilbyr to implementeringer av dette grensesnittet, avhengig av hvordan vi definerer databasetilkoblingene:

  • Ved hjelp av DataSource-grensesnittet fra Java - vi bruker DataSourceBasedMultiTenantConnectionProviderImpl gjennomføring
  • Bruker ConnectionProvider grensesnitt fra dvalemodus - vi vil bruke AbstractMultiTenantConnectionProvider gjennomføring

3.2.CurrentTenantIdentifierResolver

Det er mange mulige måter å løse en leietakeridentifikator på. For eksempel kan implementeringen vår bruke en leietakeridentifikator definert i en konfigurasjonsfil.

En annen måte kan være å bruke leieridentifikatoren fra en styparameter.

La oss se dette grensesnittet:

offentlig grensesnitt CurrentTenantIdentifierResolver {Streng løseCurrentTenantIdentifier (); boolsk validateExistingCurrentSessions (); }

Dvalemodus kaller metoden resolCurrentTenantIdentifier for å få leietakeridentifikatoren. Hvis vi vil at dvalemodus skal validere alle eksisterende økter tilhører samme leieridentifikator, er metoden validateExistingCurrentSessions skal returnere sant.

4. Skjema-tilnærming

I denne strategien bruker vi forskjellige skjemaer eller brukere i samme fysiske databaseinstans. Denne tilnærmingen bør brukes når vi trenger best ytelse for applikasjonen vår og kan ofre spesielle databasefunksjoner som sikkerhetskopi per leietaker.

Vi vil også spotte CurrentTenantIdentifierResolver grensesnitt for å gi en leietakeridentifikator som vårt valg under testen:

offentlig abstrakt klasse MultitenancyIntegrationTest {@Mock private CurrentTenantIdentifierResolver currentTenantIdentifierResolver; privat SessionFactory sessionFactory; @Før offentlige ugyldig oppsett () kaster IOException {MockitoAnnotations.initMocks (dette); når (currentTenantIdentifierResolver.validateExistingCurrentSessions ()) .thenReturn (false); Egenskaper egenskaper = getHibernateProperties (); properties.put (AvailableSettings.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolver); sessionFactory = buildSessionFactory (egenskaper); initTenant (TenantIdNames.MYDB1); initTenant (TenantIdNames.MYDB2); } beskyttet ugyldig initTenant (String tenantId) {når (currentTenantIdentifierResolver .resolveCurrentTenantIdentifier ()) .thenReturn (tenantId); createCarTable (); }}

Vår implementering av MultiTenantConnectionProvider grensesnitt vil angi skjemaet som skal brukes hver gang en tilkobling blir bedt om:

klasse SchemaMultiTenantConnectionProvider utvider AbstractMultiTenantConnectionProvider {private ConnectionProvider connectionProvider; public SchemaMultiTenantConnectionProvider () kaster IOException {this.connectionProvider = initConnectionProvider (); } @ Override-beskyttet ConnectionProvider getAnyConnectionProvider () {return connectionProvider; } @ Override-beskyttet ConnectionProvider selectConnectionProvider (String tenantIdentifier) ​​{return connectionProvider; } @ Override public Connection getConnection (String tenantIdentifier) ​​kaster SQLException {Connection connection = super.getConnection (tenantIdentifier); connection.createStatement () .execute (String.format ("SET SCHEMA% s;", tenantIdentifier)); returforbindelse; } private ConnectionProvider initConnectionProvider () kaster IOException {Properties egenskaper = nye egenskaper (); properties.load (getClass () .getResourceAsStream ("/ hibernate.properties")); DriverManagerConnectionProviderImpl connectionProvider = ny DriverManagerConnectionProviderImpl (); connectionProvider.configure (egenskaper); tilbakekobling Tilbyder; }}

Så vi bruker en H2-database i minnet med to skjemaer - en per hver leietaker.

La oss konfigurere dvalemodus. eiendommer for å bruke skjemaet multitenancy-modus og vår implementering av MultiTenantConnectionProvider grensesnitt:

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

I forbindelse med testen har vi konfigurert dvalemodus.forbindelse.url eiendom for å lage to skjemaer. Dette bør ikke være nødvendig for en reell applikasjon, siden skjemaene allerede skal være på plass.

For testen vår legger vi til en Bil oppføring i leietaker myDb1. Vi bekrefter at denne oppføringen ble lagret i databasen vår, og at den ikke er i leieren myDb2:

@Test ugyldig nårAddingEntries_thenOnlyAddedToConcreteDatabase () {whenCurrentTenantIs (TenantIdNames.MYDB1); nårAddCar ("myCar"); thenCarFound ("myCar"); whenCurrentTenantIs (TenantIdNames.MYDB2); thenCarNotFound ("myCar"); }

Som vi kan se i testen, bytter vi leietaker når vi ringer til whenCurrentTenantIs metode.

5. Databasetilnærming

Tilnærmingen til databasen med flere leieforhold bruker forskjellige fysiske databaseforekomster per leietaker. Siden hver leietaker er helt isolert, vi bør velge denne strategien når vi trenger spesielle databasefunksjoner som sikkerhetskopi per leietaker mer enn vi trenger best ytelse.

For databasetilnærmingen bruker vi det samme MultitenancyIntegrationTest klasse og CurrentTenantIdentifierResolver grensesnitt som ovenfor.

For MultiTenantConnectionProvider bruker vi en Kart samling for å få en ConnectionProvider per leietakeridentifikator:

klasse MapMultiTenantConnectionProvider utvider AbstractMultiTenantConnectionProvider {private Map connectionProviderMap = new HashMap (); public MapMultiTenantConnectionProvider () kaster IOException {initConnectionProviderForTenant (TenantIdNames.MYDB1); initConnectionProviderForTenant (TenantIdNames.MYDB2); } @ Override-beskyttet ConnectionProvider getAnyConnectionProvider () {return connectionProviderMap.values ​​() .iterator () .next (); } @ Override-beskyttet ConnectionProvider selectConnectionProvider (String tenantIdentifier) ​​{return connectionProviderMap.get (tenantIdentifier); } private void initConnectionProviderForTenant (String tenantId) kaster IOException {Properties egenskaper = nye egenskaper (); properties.load (getClass (). getResourceAsStream (String.format ("/ hibernate-database-% s.properties", tenantId))); DriverManagerConnectionProviderImpl connectionProvider = ny DriverManagerConnectionProviderImpl (); connectionProvider.configure (egenskaper); this.connectionProviderMap.put (tenantId, connectionProvider); }}

Hver ConnectionProvider fylles ut via konfigurasjonsfilen dvale-database-.egenskaper, som har alle tilkoblingsdetaljer:

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

Til slutt, la oss oppdatere dvalemodus. eiendommer igjen for å bruke databasen multitenancy-modus og vår implementering av MultiTenantConnectionProvider grensesnitt:

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

Hvis vi kjører nøyaktig samme test som i skjematilnærmingen, går testen igjen.

6. Konklusjon

Denne artikkelen dekker støtte for dvalemodus 5 for multitenancy ved hjelp av den separate databasen og separate skjematilnærminger. Vi gir veldig enkle implementeringer og eksempler for å undersøke forskjellene mellom disse to strategiene.

De fullstendige kodeprøvene som brukes i denne artikkelen er tilgjengelige på GitHub-prosjektet vårt.