So implementieren Sie eine Multi Tenant Spring Boot-Anwendung (jeder Benutzer hat eine eigene Datenbank)
Ich erstelle eine REST-API mit Spring Boot und möchte eine Multi-Tenant-Struktur für den Umgang mit Daten implementieren. Ich möchte eine Datenbank namens Main
das haben wird User
Tabelle, die Daten über Benutzer hava werden (Benutzername, Passwort ... und ein Feld , database
das die Datenbank ernannt dieses Benutzers bezeichnen wird). Jedes Mal, wenn ein Benutzer sich anmeldet, wird seine jeweilige Datenbank erstellt (dies ist einer der Punkte, an denen ich auf Schwierigkeiten stoße). Ich habe verschiedene Tutorials gelesen und alle sind mit vordefinierten Datasource
s in der application.properties
Datei. Dies ist hier eindeutig nicht der Fall, da die Datenbank für jeden Benutzer "on the fly" erstellt wird oder auf sie zugegriffen wird, wenn sie bereits erstellt wurde.
Der Workflow sieht folgendermaßen aus (so einfach wie möglich erklärt):
- Benutzeranmeldung
- Die App erstellt die Benutzerentität, speichert sie in der
Main
Datenbank und erstellt die entsprechende Datenbank für den Benutzer - Die App prüft bei jedem Anruf, ob der Benutzer authentifiziert ist, und holt dann Daten aus seiner Datenbank
Dann gibt es viele Fragen zum Füllen der DBs, wenn diese automatisch erstellt werden. Aber das Wichtigste zuerst :)
Mein Stack: POSTGRESQL, Spring Boot
Danke im Voraus.
Antworten
Die Mandantenfähigkeit kann nach Bedarf erreicht werden, indem Sie die folgenden Schritte ausführen.
- Fügen Sie zwei Konfigurationsklassen hinzu, eine für die gemeinsam genutzte Datenbank und eine für die Mandantendatenbank, mit der LocalContainerEntityManagerFactoryBean konfiguriert wird. Diese Bean sollte die erforderlichen Mandantenfähigkeitseigenschaften für LocalContainerEntityManagerFactoryBean festlegen, z
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");
Diese Klasse sollte auch den benannten Bean transactionManager für jeden Typ implementieren. z.B
@Bean(name = "tenantTransactionManager")
public PlatformTransactionManager transactionManager() {
JpaTransactionManager tm = new JpaTransactionManager();
tm.setEntityManagerFactory(this.entityManagerFactory().getObject());
return tm;
}
Implementieren Sie die Schnittstelle CurrentTenantIdentifierResolver und die Methode resolveCurrentTenantIdentifier. Dies sollte den Datenbanknamen des Mandanten basierend auf dem aktuell angemeldeten Benutzer zurückgeben. Oder Standarddatenbankname, wenn kein Benutzer angemeldet ist
Ein Thread-sicherer Kontextinhaber, der sich den aktuellen Mandantennamen merkt
Kommentieren Sie die Service-Implementierungen für die Entitätsklassen mit @Transactional Annotation und übergeben Sie den Bean-Namen des entsprechenden Entitätsmanagers, z
@Transactional("tenantTransactionManager") // for tenant database
und
@Transactional("transactionManager") // for shared database.
Richten Sie eine Methode zur Erstellung eines Datenbankschemas ein, wenn sich ein neuer Benutzer anmeldet. und pflegen Sie den Namen der Mandantendatenbank als eine der Spalten in der Benutzertabelle im gemeinsam genutzten Schema.
Wenn Sie Spring Security verwenden, implementieren Sie die UserDetailsService-Schnittstelle und implementieren Sie die Methode loadUserByUsername so, dass ein Objekt der TenantUser-Klasse zurückgegeben wird, das zusätzliche Informationen (Tenant-Datenbankname) für die Benutzeranmeldung enthält.
public class TenantUser extends org.springframework.security.core.userdetails.User {
/** The tenand id. */
private String tenantId;
Hoffe, diese Schritte helfen Ihnen, das zu erreichen, was Sie wollen. Es sind viele Artikel verfügbar, in denen alle diese Schritte ausführlich erläutert werden. Meine Implementierung ist tief in mein Projekt eingebettet, daher befindet sie sich nicht in einem Zustand, der als Arbeitsbeispiel geteilt werden kann.
Gerne beantworten wir weitere Fragen
Die vollständige Lösung für mein Problem habe ich hier gefunden:
Mandantenfähigkeit: Verwalten mehrerer Datenquellen mit Spring Data JPA
Vielen Dank an den Autor @ Cepr0.
Das einzige, was fehlt, ist das Erstellen der Datenbank im laufenden Betrieb. Ich werde die Antwort hier aktualisieren, wenn ich meine Implementierung abgeschlossen habe.
AKTUALISIEREN
Ich habe die Datenbank mit dem folgenden Code erstellt, der von @Milind Barve empfohlen wurde. Also vielen Dank.
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");
UPDATE: Beim Initialisieren des Schemas jeder neu erstellten Datenbank habe ich eine SQL-Datei mit allen Tabellenerstellungen erstellt und FlyWay verwendet, um jede neu erstellte Datenbank zu initialisieren
// INITIALIZE THE DB
Flyway flyway = Flyway.configure()
.dataSource(dataSource)
.target(MigrationVersion.LATEST)
.load();
flyway.migrate();