Jak wdrożyć aplikację Multi Tenant Spring Boot (każdy użytkownik ma własną bazę danych)
Buduję REST-API z rozruchem sprężynowym i chciałbym zaimplementować strukturę Multi Tenant do obsługi danych. Chcę mieć jedną bazę danych o nazwie, Main
która będzie miała User
tabelę, która będzie zawierała dane o użytkownikach (nazwa użytkownika, hasło ... i pole, database
które będzie określać, która baza danych jest przypisana temu użytkownikowi). Za każdym razem, gdy użytkownik zarejestruje się, zostanie utworzona jego odpowiednia baza danych (jest to jeden z punktów, w których mam trudności). Przeczytałem różne samouczki i wszystkie mają predefiniowane Datasource
s w application.properties
pliku. Oczywiście tak nie jest w tym przypadku, ponieważ baza danych dla każdego użytkownika zostanie utworzona „w locie” lub będzie dostępna, jeśli została już utworzona.
Przepływ pracy jest taki (wyjaśniony tak prosto, jak to tylko możliwe):
- Rejestracja użytkownika
- Aplikacja tworzy jednostkę użytkownika, zapisuje ją w
Main
bazie danych i tworzy odpowiednią bazę danych dla użytkownika - Aplikacja sprawdza przy każdym wywołaniu, czy użytkownik jest uwierzytelniony, a jeśli tak, to pobiera dane z jego bazy danych
Następnie pojawia się wiele pytań dotyczących wypełniania baz danych, gdy są one tworzone automatycznie. Ale najpierw wszystko :)
Mój stos: POSTGRESQL, Spring Boot
Z góry dziękuję.
Odpowiedzi
Obsługa wielu najemców może zostać osiągnięta zgodnie z wymaganiami, wykonując następujące kroki.
- Dodaj 2 klasy konfiguracji, jedną dla udostępnionej bazy danych i jedną dla bazy danych dzierżawców, która konfiguruje LocalContainerEntityManagerFactoryBean. Ten komponent bean powinien ustawić wymagane właściwości obsługi wielu dzierżawców dla LocalContainerEntityManagerFactoryBean, np.
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");
Ta klasa powinna również implementować nazwany komponent bean transactionManager dla każdego typu. na przykład
@Bean(name = "tenantTransactionManager")
public PlatformTransactionManager transactionManager() {
JpaTransactionManager tm = new JpaTransactionManager();
tm.setEntityManagerFactory(this.entityManagerFactory().getObject());
return tm;
}
implementacja interfejsu CurrentTenantIdentifierResolver i metody solutionCurrentTenantIdentifier. Powinno to zwrócić nazwę bazy danych dzierżawy na podstawie aktualnie zalogowanego użytkownika. Lub domyślna nazwa bazy danych, jeśli żaden użytkownik nie jest zalogowany
Uchwyt kontekstu bezpiecznego wątku do zapamiętania bieżącej nazwy dzierżawcy
Zanotuj implementacje usług dla klas jednostek za pomocą adnotacji @Transactional i przekaż nazwę fasoli odpowiedniego menedżera encji, np.
@Transactional("tenantTransactionManager") // for tenant database
i
@Transactional("transactionManager") // for shared database.
Skonfiguruj metodę tworzenia schematu bazy danych, gdy rejestruje się nowy użytkownik. i zachowaj nazwę bazy danych dzierżawcy jako jedną z kolumn w tabeli użytkowników w schemacie współużytkowanym.
Jeśli korzystasz z zabezpieczeń Spring, zaimplementuj interfejs UserDetailsService i zaimplementuj metodę loadUserByUsername tak, aby zwracała obiekt klasy TenantUser, który zawiera dodatkowe informacje (nazwa bazy danych dzierżawy) dla logowania użytkownika.
public class TenantUser extends org.springframework.security.core.userdetails.User {
/** The tenand id. */
private String tenantId;
Mam nadzieję, że te kroki pomogą Ci osiągnąć to, czego chcesz. Dostępnych jest wiele artykułów, które szczegółowo wyjaśniają wszystkie te kroki. Moja implementacja jest głęboko osadzona w moim projekcie, stąd nie jest w stanie, który można by udostępnić jako działający przykład.
Chętnie odpowiem na dalsze pytania
Tutaj znalazłem kompletne rozwiązanie mojego problemu:
Multi-tenancy: zarządzanie wieloma źródłami danych za pomocą Spring Data JPA
Wielkie dzięki dla autora @ Cepr0.
Jedyne, czego brakuje, to tworzenie bazy danych w locie. Zaktualizuję odpowiedź tutaj, kiedy zakończę wdrażanie.
AKTUALIZACJA
Bazę danych stworzyłem z następującym kodem, który polecił @Milind Barve. Więc dziękuję.
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");
AKTUALIZACJA: Inicjując schemat każdej nowo utworzonej bazy danych, utworzyłem plik .sql ze wszystkimi kreacjami tabeli i używam FlyWay do zainicjowania każdej nowo utworzonej bazy danych
// INITIALIZE THE DB
Flyway flyway = Flyway.configure()
.dataSource(dataSource)
.target(MigrationVersion.LATEST)
.load();
flyway.migrate();