web-dev-qa-db-fra.com

Spring-data-mongodb se connecte à plusieurs bases de données dans une instance Mongo

J'utilise la dernière version de spring-data-mongodb (1.1.0.M2) et la dernière version de Mongo Driver (2.9.0-RC1). Dans certains cas, plusieurs clients se connectent à mon application et je souhaite attribuer à chacun leur propre "schéma/base de données" sur le même serveur Mongo. Ce n’est pas une tâche très difficile à accomplir si j’utilisais directement le pilote:

Mongo mongo = new Mongo( new DBAddress( "localhost", 127017 ) );

DB client1DB = mongo.getDB( "client1" );
DBCollection client1TTestCollection = client1DB.getCollection( "test" );
long client1TestCollectionCount = client1TTestCollection.count();

DB client2DB = mongo.getDB( "client2" );
DBCollection client2TTestCollection = client2DB.getCollection( "test" );
long client2TestCollectionCount = client2TTestCollection.count();

Voir, facile. Cependant, spring-data-mongodb ne permet pas d’utiliser facilement plusieurs bases de données. Le moyen préféré pour établir une connexion avec Mongo consiste à étendre le AbstractMongoConfiguration class: 

Vous verrez que vous substituez la méthode suivante:

getDatabaseName()

Cela vous oblige donc à utiliser un nom de base de données. Les interfaces de référentiel que vous créez ensuite utilisent ce nom de base de données à l'intérieur du modèle MongoTemplate transmis à la classe SimpleMongoRepository.

Où puis-je coller plusieurs noms de base de données? Je dois créer plusieurs noms de base de données, plusieurs MongoTempates (un par nom de base de données) et plusieurs autres classes de configuration. Et cela ne permet toujours pas à mes interfaces de référentiel d'utiliser le bon modèle. Si quelqu'un a essayé une telle chose, faites le moi savoir. Si je le découvre, je posterai la réponse ici.

Merci.

20
sbzoom

Donc, après de nombreuses recherches et expérimentations, j'ai conclu qu'il ne s'agissait pas encore du projet spring-data-mongodb actuel. J'ai essayé la méthode de baja ci-dessus et je me suis heurté à un obstacle spécifique. MongoTemplate exécute sa méthode ensureIndexes() à partir de son constructeur. Cette méthode appelle la base de données pour vérifier que des index annotés existent dans la base de données. Le constructeur de MongoTemplate est appelé lorsque Spring démarre, je n'ai donc jamais la possibilité de définir une variable ThreadLocal. Une valeur par défaut doit déjà être définie au démarrage de Spring, puis modifiez-la lorsqu'une demande arrive. Ce n'est pas autorisé, car je ne veux pas et je n'ai pas de base de données par défaut.

Tout n'était pas perdu cependant. Notre plan initial consistait à faire en sorte que chaque client s'exécute sur son propre serveur d'applications, pointé vers sa propre base de données MongoDB sur le serveur MongoDB. Ensuite, nous pouvons fournir une variable système -Dprovider= et chaque serveur s’exécutant pointant uniquement sur une base de données.

On nous a demandé d'avoir une application multi-locataire, d'où la tentative de la variable ThreadLocal. Mais comme cela ne fonctionnait pas, nous avons pu exécuter l'application telle que nous l'avions conçue à l'origine.

Je crois qu’il existe un moyen de tout faire fonctionner, mais il en faut plus que ce qui est décrit dans les autres articles. Vous devez faire votre propre RepositoryFactoryBean. Voici l'exemple de Spring Data Documents de référence MongoDB . Vous devez toujours implémenter votre propre variable MongoTemplate et retarder ou supprimer l'appel ensureIndexes(). Mais vous devrez réécrire quelques classes pour vous assurer que votre MongoTemplate est appelée à la place de Spring's. En d'autres termes, beaucoup de travail. Travail que je voudrais voir arriver ou même faire, je n’avais tout simplement pas le temps.

Merci pour les réponses.

8
sbzoom

Voici un lien vers un article que vous recherchez, je pense http://michaelbarnesjr.wordpress.com/2012/01/19/spring-data-mongo/

La clé est de fournir plusieurs modèles

configurer un modèle pour chaque base de données.

<bean id="vehicleTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg ref="mongoConnection"/>
    <constructor-arg name="databaseName" value="vehicledatabase"/>
</bean>

configurer un modèle pour chaque base de données.

<bean id="imageTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg ref="mongoConnection"/>
        <constructor-arg name="databaseName" value="imagedatabase"/>
</bean>

<bean id="vehicleTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg ref="mongoConnection"/>
    <constructor-arg name="databaseName" value="vehicledatabase"/>
</bean>

Maintenant, vous devez indiquer à Spring où se trouvent vos référentiels afin de pouvoir les injecter. Ils doivent tous être dans le même répertoire. J'ai essayé de les avoir dans différents sous-répertoires, et cela ne fonctionnait pas correctement. Donc, ils sont tous dans le répertoire du référentiel.

<mongo:repositories base-package="my.package.repository">
    <mongo:repository id="imageRepository" mongo-template-ref="imageTemplate"/>
    <mongo:repository id="carRepository" mongo-template-ref="vehicleTemplate"/>
    <mongo:repository id="truckRepository" mongo-template-ref="vehicleTemplate"/>
</mongo:repositories>

Chaque référentiel est une interface et est écrit comme suit (oui, vous pouvez les laisser vides):

@Repository
public interface ImageRepository extends MongoRepository<Image, String> {

}

@Repository
public interface TruckRepository extends MongoRepository<Truck, String> {

}

Le nom de la variable privée imageRepository est la collection! Image.Java sera enregistré dans la collection d'images de la base de données imagedb.

Voici comment vous pouvez trouver, insérer et supprimer:

@Service
public class ImageService {

    @Autowired
    private ImageRepository imageRepository;
}

Avec le câblage automatique, vous faites correspondre le nom de la variable au nom (id) de votre configuration.

14
john

Vous souhaiterez peut-être sous-classer SimpleMongoDbFactory et définir une stratégie pour renvoyer la base de données par défaut renvoyée par getDb. Une option consiste à utiliser des variables locales aux threads pour décider de la base de données à utiliser, au lieu d'utiliser plusieurs modèles MongoTemplates.

Quelque chose comme ça:

public class ThreadLocalDbNameMongoDbFactory extends SimpleMongoDbFactory {
    private static final ThreadLocal<String> dbName = new ThreadLocal<String>();
    private final String defaultName; // init in c'tor before calling super

    // omitted constructor for clarity

    public static void setDefaultNameForCurrentThread(String tlName) {
        dbName.set(tlName);
    }
    public static void clearDefaultNameForCurrentThread() {
        dbName.remove();
    }

    public DB getDb() {
        String tlName = dbName.get();
        return super.getDb(tlName != null ? tlName : defaultName);
    }
}

Ensuite, remplacez mongoDBFactory() dans votre classe @Configuration qui s'étend de AbstractMongoConfiguration comme ceci:

@Bean
@Override
public MongoDbFactory mongoDbFactory() throws Exception {
  if (getUserCredentials() == null) {
      return new ThreadLocalDbNameMongoDbFactory(mongo(), getDatabaseName());
  } else {
      return new ThreadLocalDbNameMongoDbFactory(mongo(), getDatabaseName(), getUserCredentials());
  }
}

Dans votre code client (peut-être un ServletFilter ou autre), vous devrez appeler: ThreadLocalDBNameMongoRepository.setDefaultNameForCurrentThread() Avant de commencer tout travail en Mongo, puis le réinitialiser avec: ThreadLocalDBNameMongoRepository.clearDefaultNameForCurrentThread()

8
baja

L’endroit à regarder est l’interface MongoDbFactory. L'implémentation de base de cela prend une instance Mongo et fonctionne avec cela pendant toute la durée de vie de l'application. Pour obtenir une utilisation de base de données par thread (et donc par requête), vous devrez probablement implémenter quelque chose du type AbstractRoutingDataSource . L’idée est qu’il existe une méthode de modèle qui doit rechercher le locataire par invocation (ThreadLocal lié, je suppose), puis sélectionner une instance Mongo dans un ensemble prédéfini ou une logique personnalisée pour un nouveau locataire etc.

Gardez à l'esprit que MongoDbFactory est généralement utilisé avec la méthode getDb(). Cependant, certaines fonctionnalités de MongoDB nécessitent que nous fournissions une getDb(String name). DBRefs (comme une clé étrangère dans le monde relationnel) peut pointer vers des documents d'une base de données entièrement différente. Donc, si vous faites la délégation, évitez d’utiliser cette fonctionnalité (je pense que les DBRefs pointant vers une autre base de données sont les seuls emplacements appelant getDb(name)) ou gèrent explicitement cette fonction.

Du point de vue de la configuration, vous pouvez soit simplement remplacer entièrement mongoDbFactory(), soit simplement ne pas étendre la classe de base du tout et créer votre propre configuration basée sur Java.

4
Oliver Drotbohm

J'ai utilisé différentes bases de données en utilisant Java Config, voici comment je l'ai fait:

@Bean 
public MongoDbFactory mongoRestDbFactory() throws Exception { 
    MongoClientURI uri=new MongoClientURI(environment.getProperty("mongo.uri")); 
    return new SimpleMongoDbFactory(uri);
}

@Override
public String getDatabaseName() {
    return "rest";
}

@Override
public @Bean(name = "secondaryMongoTemplate") MongoTemplate mongoTemplate() throws Exception{ //hay que cambiar el nombre de los templates para que el contendor de beans sepa la diferencia  
    return new MongoTemplate(mongoRestDbFactory());    
}

Et l'autre était comme ça: 

@Bean 
public MongoDbFactory restDbFactory() throws Exception {
    MongoClientURI uri = new MongoClientURI(environment.getProperty("mongo.urirestaurants")); 
    return new SimpleMongoDbFactory(uri);
}

@Override
public String getDatabaseName() {
    return "rest";
}

@Override
public @Bean(name = "primaryMongoTemplate") MongoTemplate mongoTemplate() throws Exception{ 
    return new MongoTemplate(restDbFactory());    
}

Ainsi, lorsque je dois modifier ma base de données, je ne sélectionne que la configuration à utiliser.

1
user3272931