1
votes

Besoin de modifier dynamiquement la base de données Nom de l'url de la source de données (base de données multi-tenant)

J'utilise Spring boot et Spring-data-Jpa. Je définis l'URL, le nom d'utilisateur et le mot de passe de ma source de données dans le fichier application.properties. Cela fonctionne parfaitement pour une connexion à une base de données, maintenant je suis confronté à un problème avec la structure de mon projet de base de données qui est basée sur l'utilisateur particulier dont sa propre base de données a besoin pour se connecter et obtenir le résultat à une base de données utilisateur particulière et je peux y parvenir en utilisant une source de données abstraite, DataSourceBuilder au niveau de la configuration (c'est une fois que je peux changer la source de données dynamiquement) mais j'ai besoin de changer la source de données chaque fois que le contrôleur frappe.

voici du code pour application.properties et j'ai injecté ma source de données en utilisant autowire.

source de données abstraite que j'ai utilisée et elle est limitée au client statique, mais dans ma structure la base de données des clients continue d'augmenter, donc ce n'est pas utile pour moi

spring.datasource.url = jdbc: sqlserver: // test-datbase: 1433; dbName1 spring.datasource.username = nom_utilisateur spring.datasource.password = Mot de passe spring.datasource.driverClassName = com.microsoft.sqlserver.jdbc.SQLServerDriver

Besoin d'un code ou d'une méthode Je peux modifier ma connexion à la base de données à chaque coup sur le contrôleur

Remarque: j'ai juste besoin de changer ma base de données, mon dialecte et tout le reste sera le même.


3 commentaires

Pouvez-vous reformuler et modifier votre question? Utilisez-en. et, au lieu d'une grande phrase.


@ M.Deinum j'ai changé ma phrase


Vous pouvez utiliser le filtrage d'url pour établir une connexion différente au moment de l'exécution, en fonction de l'url d'où provient la demande, vous pouvez la vérifier puis vous connecter à la base de données respective, vous pouvez également créer les multiples objets dataSourceConnection de différentes bases de données lorsque l'application démarre et stocker dans un HashMap et créer un bean, vous pouvez ensuite créer automatiquement cet objet dans votre projet pour obtenir la connexion de base de données différente.


5 Réponses :


0
votes

Voici comment résoudre ce problème: Vous pouvez créer 2 sources de données distinctes. Créez des qualificatifs pour eux et injectez tous les deux dans votre contrôleur. Ensuite, dans la logique d'écriture du point final, qui sélectionnerait l'une des sources pour enregistrer les informations.

Voici comment ajouter une source de données supplémentaire à votre projet:

https: // medium.com/@joeclever/using-multiple-datasources-with-spring-boot-and-spring-data-6430b00c02e7


1 commentaires

merci pour la réponse, ce n'est pas utile pour mon problème, dans votre cas, je dois spécifier la source de données au stade initial, c'est-à-dire que je dois spécifier ma liste de source de données mais actuellement ma structure est basée sur un utilisateur, j'obtiendrai une connexion à la base détails d'une base de données à partir de là, je me connecte à une autre base de données.



0
votes

Je pense que c'est une bonne idée d'utiliser Wildfly dans cette situation. Chez Wildfly, vous pouvez modifier la base de données connectée à l'aide des paramètres.

Ma solution: entrez la description du lien ici

et merci de créer votre propre classe PersistenceConfiguration lorsque vous choisissez la base de données 3 commentaires

Je ne pense pas que votre solution fonctionne, j'ai besoin de créer une source de données au moment de l'exécution et d'appeler la base de données à l'aide de l'objet Spring Data Jpa Persistence EntityManager, votre solution peut également être réalisée via une source de données abstraite.


Dans mon application, j'ai défini DataSource dans DEVPersistenceContext.class ( github.com/TheBestSoftInTheWorld/... ). Si vous souhaitez le modifier dynamiquement, veuillez le faire dans la classe DEVPersistenceContext.java


dans le fichier de persistance, écrivez url1 = app.datasource.url = jdbc: postgresql: // localhost: 5432 / bas‌ e1 url2 = app.datasource.url = jdbc: postgresql: // localhost: 5432 / bas‌ e2 et choisissez la base de données dans DEVPersistenceContext.class ( github.com/TheBestSoftInTheWorld/... DataSourceBuilder .url (env.getProperty ("url1"))



1
votes

Oui, nous pouvons le faire en utilisant un espace réservé. Définissez -DdbName1 = VOTRE_DB_NAME dans les variables d'environnement. Par exemple:

spring.datasource.url=jdbc:sqlserver://test-datbase:1433;${dbName1}
spring.datasource.username=userName
spring.datasource.password=Password
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver


3 commentaires

Merci pour votre réponse. Puis-je activer la base de données à chaque fois que l'utilisateur accède au contrôleur? Je pense que ce que vous avez donné est un changement de temps de base de données.


Ce n'est pas possible. Contexte d'application initialisé au démarrage du serveur. Construire un nouveau contexte sur chaque hit du contrôleur ralentira les performances. Je ne l'ai pas fait mais avec quelques changements, nous pouvons le faire, mais ce n'est pas une approche recommandée.


mais comment d'autres microservices sont-ils capables de se connecter à plusieurs locataires?



0
votes

avec l'aide du lien ci-dessous, je peux définir ma source de données multiple au démarrage du serveur

https://fizzylogic.nl/2016/01/24/make-your-spring-boot-application-multi-tenant -aware-in-2-steps /

mais je veux supprimer l'annotation de configuration comme ci-dessous et définir mon locataire en utilisant la méthode ci-dessous, mais à travers cela, je ne suis pas en mesure de me connecter à la base de données.

public class MultitenantConfiguration {
  @Bean
@ConfigurationProperties(
        prefix = "spring.datasource"
)
public DataSource dataSource(ArrayList<String> names) {


    Map<Object,Object> resolvedDataSources = new HashMap<>();

    for(String  dbName: names) {

        DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(this.getClass().getClassLoader());
        dataSourceBuilder.driverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver")
                .url("jdbc:sqlserver://abc.server;databaseName="+dbName+"")
                .username("userName")
                .password("Password");


        resolvedDataSources.put(dbName, dataSourceBuilder.build());
    }



    MultitenantDataSource dataSource = new MultitenantDataSource();
    dataSource.setDefaultTargetDataSource(defaultDataSource());
    dataSource.setTargetDataSources(resolvedDataSources);
    dataSource.afterPropertiesSet();


    return dataSource;
}

/**
 * Creates the default data source for the application
 * @return
 */
private DataSource defaultDataSource() {
    DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(this.getClass().getClassLoader())
            .driverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver")
            .url("jdbc:abc.server;databaseName=test")
            .username("UserName")
            .password("Password");

    return dataSourceBuilder.build();
}
  }


0 commentaires

0
votes

J'ai fait un projet dans lequel je peux créer plusieurs dataSources avec vos changesSets spécifiques, donc si vous avez besoin d'ajouter une autre dataSource, cela changerait simplement votre application.yml, plus besoin de changer le code. Mais si vous ne l'utilisez pas, retirez simplement la liquibase qui fonctionne aussi!

Sur chaque hit pour votre contrôleur, vous devez obtenir un en-tête X-TenantId, qui changera votre ThreadLocal, qui à son tour change la source de données en fonction du locataire

Code complet: https://github.com/dijalmasilva/spring-boot-multitenancy-datasource-liquibase

@Configuration
@ConditionalOnProperty(prefix = "spring.liquibase", name = "enabled", matchIfMissing = true)
@EnableConfigurationProperties(LiquibaseProperties.class)
@AllArgsConstructor
public class LiquibaseConfiguration {

    private LiquibaseProperties properties;
    private DataSourceProperties dataSourceProperties;

    @Bean
    @DependsOn("tenantRoutingDataSource")
    public MultiTenantDataSourceSpringLiquibase liquibaseMultiTenancy(Map<Object, Object> dataSources,
                                                                      @Qualifier("taskExecutor") TaskExecutor taskExecutor) {
        // to run changeSets of the liquibase asynchronous
        MultiTenantDataSourceSpringLiquibase liquibase = new MultiTenantDataSourceSpringLiquibase(taskExecutor);
        dataSources.forEach((tenant, dataSource) -> liquibase.addDataSource((String) tenant, (DataSource) dataSource));
        dataSourceProperties.getDataSources().forEach(dbProperty -> {
            if (dbProperty.getLiquibase() != null) {
                liquibase.addLiquibaseProperties(dbProperty.getTenantId(), dbProperty.getLiquibase());
            }
        });

        liquibase.setContexts(properties.getContexts());
        liquibase.setChangeLog(properties.getChangeLog());
        liquibase.setDefaultSchema(properties.getDefaultSchema());
        liquibase.setDropFirst(properties.isDropFirst());
        liquibase.setShouldRun(properties.isEnabled());
        return liquibase;
    }

}

public class TenantFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        final String X_TENANT_ID = "X-TenantID";

        final HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        final String tenantId = httpServletRequest.getHeader(X_TENANT_ID);

        if (tenantId == null) {
            final HttpServletResponse response = (HttpServletResponse) servletResponse;
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.getWriter().write("{\"error\": \"No tenant header supplied\"}");
            response.getWriter().flush();
            TenantContext.clear();
            return;
        }

        TenantContext.setCurrentTenant(tenantId);
        filterChain.doFilter(servletRequest, servletResponse);
    }
}


0 commentaires