Comment implémenter une application Multi Tenant Spring Boot (chaque utilisateur a sa propre base de données)
Je construis une API REST avec Spring Boot et je voudrais implémenter une structure multi-locataire pour gérer les données. Je veux avoir une base de données appelée Main
qui aura une User
table, qui contiendra des données sur les utilisateurs (nom d'utilisateur, mot de passe ... et un champ database
qui indiquera quelle base de données est désignée pour cet utilisateur). Chaque fois qu'un utilisateur s'inscrit, son DB respectif sera créé (c'est l'un des points où je rencontre des difficultés). J'ai lu différents tutoriels et ils sont tous avec des Datasource
s prédéfinis dans le application.properties
fichier. Ce n'est clairement pas le cas ici, puisque la base de données de chaque utilisateur sera créée "à la volée", ou accessible si elle est déjà créée.
Le flux de travail est comme ceci (expliqué aussi simple que possible):
- Inscription des utilisateurs
- L'application crée l'entité utilisateur, l'enregistre dans la base de
Main
données et crée la base de données respective pour l'utilisateur - L'application vérifie à chaque appel si l'utilisateur est authentifié, s'il l'est, puis va chercher les données de sa base de données
Ensuite, il y a beaucoup de questions concernant le remplissage des bases de données lors de leur création automatique. Mais tout d'abord :)
Ma pile: POSTGRESQL, Spring Boot
Merci d'avance.
Réponses
l'hébergement multiclient peut être réalisé selon vos besoins en suivant les étapes suivantes.
- Ajoutez 2 classes de configuration, une pour la base de données partagée et une pour la base de données locataire, qui configure LocalContainerEntityManagerFactoryBean. Ce bean doit définir les propriétés de gestion mutualisée requises pour LocalContainerEntityManagerFactoryBean, par exemple
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");
Cette classe doit également implémenter le bean transactionManager nommé pour chaque type. par exemple
@Bean(name = "tenantTransactionManager")
public PlatformTransactionManager transactionManager() {
JpaTransactionManager tm = new JpaTransactionManager();
tm.setEntityManagerFactory(this.entityManagerFactory().getObject());
return tm;
}
implémentez l'interface CurrentTenantIdentifierResolver et la méthode resolutionCurrentTenantIdentifier. Cela doit renvoyer le nom de la base de données du locataire en fonction de l'utilisateur actuellement connecté. Ou le nom de la base de données par défaut si aucun utilisateur n'est connecté
Un détenteur de contexte thread-safe pour se souvenir du nom du client actuel
Annotez les implémentations de services pour les classes d'entités avec l'annotation @Transactional et passez le nom de bean du gestionnaire d'entités approprié, par exemple
@Transactional("tenantTransactionManager") // for tenant database
et
@Transactional("transactionManager") // for shared database.
Configurez une méthode de création de schéma de base de données lorsqu'un nouvel utilisateur s'inscrit. et conservez le nom de la base de données client comme l'une des colonnes de la table utilisateur dans le schéma partagé.
Si vous utilisez la sécurité Spring, implémentez l'interface UserDetailsService et implémentez la méthode loadUserByUsername de telle sorte qu'elle renvoie un objet de la classe TenantUser qui contient des informations supplémentaires (nom de la base de données du locataire) pour la connexion de l'utilisateur.
public class TenantUser extends org.springframework.security.core.userdetails.User {
/** The tenand id. */
private String tenantId;
J'espère que ces étapes vous aideront à réaliser ce que vous voulez. Il existe de nombreux articles disponibles qui expliquent toutes ces étapes en détail. Mon implémentation est profondément ancrée dans mon projet et n'est donc pas dans un état qui peut être partagé comme exemple de travail.
Heureux de répondre à toute autre question
J'ai trouvé la solution complète à mon problème ici:
Multi-tenancy: gérer plusieurs sources de données avec Spring Data JPA
Un grand merci à l'auteur @ Cepr0.
La seule chose qui manque est de créer la base de données à la volée. Je mettrai à jour la réponse ici lorsque j'aurai terminé mon implémentation.
METTRE À JOUR
J'ai créé la base de données avec le code suivant, il a été recommandé par @Milind Barve. Alors merci.
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");
MISE À JOUR: Initialisation du schéma de chaque DB nouvellement créée, j'ai créé un fichier .sql avec toutes les créations de table et utilise FlyWay pour initialiser chaque DB nouvellement créée
// INITIALIZE THE DB
Flyway flyway = Flyway.configure()
.dataSource(dataSource)
.target(MigrationVersion.LATEST)
.load();
flyway.migrate();