Changement de schéma de base de données en cours d'exécution basée sur l'utilisateur connecté
J'ai lu beaucoup de questions et réponses à propos de la dynamique de la source de données de routage et ont mis en œuvre une solution à l'aide de AbstractRoutingDataSource
et l'autre(voir ci-dessous). C'est très bien, mais nécessite codé en dur propriétés pour toutes les sources de données. Comme le nombre d'utilisateurs à l'aide de l'application augmente, ce n'est pas un moyen approprié de routage plus. Aussi il aurait besoin pour ajouter une entrée pour les propriétés à chaque fois qu'un nouvel utilisateur s'enregistre. La situation est comme suit
- 1 serveur de base de données
- de nombreux schémas sur ce serveur, chaque utilisateur dispose de son propre schéma.
- J'ai seulement besoin de changer le nom du schéma en cours d'exécution
- nom de schéma est retainable par utilisateur connecté
Je suis en utilisant spring boot 1.4.0
avec hibernate 5.1
et spring data jpa
Je ne peux pas trouver un moyen de modifier le schéma complètement de façon dynamique. Quelqu'un sait-il comment le faire au printemps?
EDIT:
Grâce à @Johannes Leimer réponse, j'ai eu un travail de mise.
Voici le code:
Utilisateur Fournisseur:
@Component
public class UserDetailsProvider {
@Bean
@Scope("prototype")
public CustomUserDetails customUserDetails() {
return (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
}
UserSchemaAwareRoutingDatasource:
public class UserSchemaAwareRoutingDataSource extends AbstractDataSource {
@Inject
Provider<CustomUserDetails> customUserDetails;
@Inject
Environment env;
private LoadingCache<String, DataSource> dataSources = createCache();
@Override
public Connection getConnection() throws SQLException {
try {
return determineTargetDataSource().getConnection();
} catch (ExecutionException e){
e.printStackTrace();
return null;
}
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
System.out.println("getConnection" + username);
System.out.println("getConnection2" + password);
try {
return determineTargetDataSource().getConnection(username, password);
} catch (ExecutionException e) {
e.printStackTrace();
return null;
}
}
private DataSource determineTargetDataSource() throws SQLException, ExecutionException {
try {
String schema = customUserDetails.get().getUserDatabase();
return dataSources.get(schema);
} catch (NullPointerException e) {
e.printStackTrace();
return dataSources.get("fooooo");
}
}
- ne voulez pas plonger profondément dans les choses, vous pouvez mettre de la logique dans la méthode qui retourne un prototype de source de données, les instances?
- La droite. Un multi-schéma de base de données fait beaucoup plus de sens que d'un multi-serveur de base de données, qui est ce que votre question annoncés avant la modification.
- Oui, modifié le texte, il a été missleading. Merci et désolé pour la mauvaise formulation! @DavidWallace
- J'ai vraiment envie de connaître la réponse de la ce. J'ai une très grosse application qui a besoin de changer de schéma pour un travail particulier. Mais à chaque fois que ce même produit, il se bloque.
- forum.printemps.io/forum/printemps-projets/data/... il couvre la plupart de la chose dont vous avez besoin. Le nom d'utilisateur, vous pouvez l'obtenir à partir du thread variable locale qui vous pouvez définir plus tôt.
- le post est de huit ans et, malheureusement, ne convient pas à mes besoins
- pouvez-vous expliquer exactement pourquoi la solution de ce thread ne correspond pas à vos souhaits? ThreadLocal contexte de l'approche est assez commun, et devrait couvrir votre cas.
- Vous pouvez essayer primordial AbstractRoutingDataSource.determineTargetDataSource() Lien méthode pour avoir un comportement différent. Si la source de données n'est pas trouvé pour votre clé, puis en créer un que par le nom de l'utilisateur et de l'insérer dans la carte, qui contient toutes les sources de données.
- Continuation pour le commentaire précédent Note Vous avez besoin d'ajouter une synchronisation correcte là pour répondre à tous les cas d'utilisation où les 2 fils de la demande pour la même source de données qui n'est pas encore créé et nous n'avons pas, en fin de compte même de la source de données à de multiples reprises.
- Pourriez-vous s'il vous plaît partagez exemple comment la mettre en œuvre celui-ci.
- Pourriez-vous s'il vous plaît partagez exemple comment la mettre en œuvre celui-ci.Il me serait utile si vous fournissez un exemple.voici mon problème stackoverflow.com/questions/51130898/...
- pourriez-vous s'il vous plaît partager repo git de POC ?
Vous devez vous connecter pour publier un commentaire.
Hypothèses
Parce que je n'ai pas la réputation encore à poster un commentaire ci-dessous votre question, ma réponse est basée sur les éléments suivants assumtions:
Le schéma actuel d'utiliser le nom de l'utilisateur actuel, est accessible par un Printemps JSR 330 Fournisseur comme
private javax.inject.Provider<User> user; String schema = user.get().getSchema();
. C'est, idéalement, d'un ThreadLocal proxy.De construire un
DataSource
qui est entièrement configuré de façon dont vous avez besoin, il exige les mêmes propriétés. Chaque fois. La seule chose qui est différente, c'est le nom du schéma. (Il serait facilement possible d'obtenir d'autres paramètres, mais ce serait trop pour cette réponse)Chaque schéma est déjà mis en place avec la nécessaire DDL, donc il n'est pas nécessaire d'hibernation pour créer des tableaux ou quelque chose d'autre
Chaque schéma de base de données semble tout à fait la même à l'exception de son nom
Vous avez besoin de réutiliser une source de données à chaque fois que l'utilisateur correspondant fait une demande à votre application. Mais vous ne voulez pas avoir à chaque source de données de chaque utilisateur de façon permanente dans la mémoire.
Ma solution à l'idée
Utiliser une combinaison de ThreadLocal proxys pour obtenir le nom du schéma et un Singleton-source de données qui se comporte de façon différente sur demande de l'utilisateur. Cette solution est inspirée par votre indice de
AbstractRoutingDataSource
, Meherzad observations et des expériences.Une dynamique
DataSource
Je suggère de faciliter la
AbstractDataSource
de Printemps et le mettre en œuvre comme leAbstractRoutingDataSource
. Au lieu de statiqueMap
approche, nous utilisons un Goyave Cache pour obtenir un facile d'utiliser le cache.Maintenant, vous avez une `source de données qui agit différent pour chaque utilisateur. Une fois une source de données est créée, elle va être mis en cache pendant 10 minutes. C'est tout.
D'en faire la demande au courant de notre dynamique de la source de données
La place pour intégrer notre nouvelle source de données est la source de données singleton connu pour le printemps contexte et utilisés dans tous les haricots par exemple, l'EntityManagerFactory
Nous avons besoin d'un équivalent à ceci:
mais il doit être plus dynamique, plus qu'un simple bien fondé DataSourceBuilder:
Conclusion
Nous avons un transparent et dynamique de la source de données qui utilise la bonne source de données à chaque fois.
Questions ouvertes
Avertissement
Je n'ai pas testé ce code!
EDIT:
Pour mettre en œuvre un
Provider<CustomUserDetails>
avec le Printemps, vous avez besoin de définir ce que prototype. Vous pouvez utiliser les Ressorts de soutien de la JSR 330 et le Printemps Securitys SecurityContextHolder:Vous n'avez pas besoin d'un
RequestInterceptor
, leUserProvider
ou le contrôleur de code pour mettre à jour l'utilisateur plus.Cela vous aide?
EDIT2
Juste pour mémoire: ne faites PAS référence à la
CustomUserDetails
bean directement. Puisque c'est un prototype, le Printemps va essayer de créer un proxy pour la classeCustomUserDetails
, ce qui n'est pas une bonne idée dans notre cas. Utilisez doncProvider
s pour accéder à ce bean. Ou faire une interface.@Component
annoté de la classe. Le@Bean
annotation est destiné à être utilisé à l'intérieur d'un@Configuration
annoté de la classe.Étant donné que vous ne spécifiez pas le SGBD, ici est une idée qui peut vous aider.
(Même si je suis en utilisant le Printemps de Données JDBC-ext comme référence, même approche peut être facilement adopté par l'utilisation générale de l'AOP)
Veuillez vous référer à http://docs.spring.io/spring-data/jdbc/docs/current/reference/html/orcl.connection.html , Section 8.2
Au Printemps de Données JDBC-ext, il est ConnectionPreparer qui peut vous permettre d'exécuter arbitraires Sql lors de l'acquisition d'une Connexion de source de données. Vous pouvez simplement exécuter les commandes pour changer de schéma (par exemple,
ALTER SESSION SET CURRENT SCHEMA = 'schemaName'
dans Oracle,using schemaName
pour Sybase, etc).par exemple
Dans l'App Contexte config
getConnection(...)
sur la source de données n'est appelée qu'une seule fois.