wake-up-neo.com

Spring-Data-Mongodb stellt eine Verbindung zu mehreren Datenbanken in einer Mongo-Instanz her

Ich verwende den neuesten Spring-Data-Mongodb (1.1.0.M2) und den neuesten Mongo Driver (2.9.0-RC1). Ich habe eine Situation, in der ich mehrere Clients mit meiner Anwendung verbunden habe und jedem ein eigenes "Schema/eine eigene Datenbank" auf demselben Mongo-Server zur Verfügung stellen möchte. Dies ist keine sehr schwierige Aufgabe, wenn ich den Treiber direkt verwendet habe:

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();

Sehen Sie einfach. Spring-data-mongodb erlaubt jedoch keine einfache Verwendung mehrerer Datenbanken. Die bevorzugte Möglichkeit, eine Verbindung zu Mongo herzustellen, besteht darin, die Klasse AbstractMongoConfiguration zu erweitern: 

Sie werden sehen, dass Sie die folgende Methode überschreiben:

getDatabaseName()

Sie müssen also nur einen Datenbanknamen verwenden. Die Repository-Schnittstellen, die Sie dann erstellen, verwenden diesen Datenbanknamen in der MongoTemplate, der an die SimpleMongoRepository-Klasse übergeben wird.

Wo in aller Welt würde ich mehrere Datenbanknamen angeben? Ich muss mehrere Datenbanknamen, mehrere MongoTempates (einen pro Datenbanknamen) und mehrere andere Konfigurationsklassen erstellen. Und das bringt meine Repository-Schnittstellen immer noch nicht dazu, die richtige Vorlage zu verwenden. Wenn jemand so etwas versucht hat, lass es mich wissen. Wenn ich es herausfinde, werde ich die Antwort hier posten.

Vielen Dank.

20
sbzoom

Nach langen Recherchen und Experimenten bin ich zu dem Schluss gekommen, dass dies mit dem aktuellen spring-data-mongodb-Projekt noch nicht möglich ist. Ich habe Bajas Methode ausprobiert und bin auf eine bestimmte Hürde gestoßen. Die Variable MongoTemplate führt ihre Methode ensureIndexes() in ihrem Konstruktor aus. Diese Methode ruft die Datenbank auf, um sicherzustellen, dass kommentierte Indizes in der Datenbank vorhanden sind. Der Konstruktor für MongoTemplate wird aufgerufen, wenn Spring gestartet wird. Ich habe also niemals die Möglichkeit, eine ThreadLocal-Variable festzulegen. Ich muss bereits einen Standardwert haben, wenn Spring gestartet wird, und dann ändern, wenn eine Anforderung eingeht. Dies ist nicht zulässig, da ich keine Standarddatenbank haben möchte oder habe.

Es war nicht alles verloren. Unser ursprünglicher Plan sah vor, dass jeder Client auf einem eigenen Anwendungsserver ausgeführt wird, der auf seine eigene MongoDB-Datenbank auf dem MongoDB-Server verweist. Dann können wir eine -Dprovider=-Systemvariable bereitstellen, und jeder Server wird nur auf eine Datenbank gerichtet.

Wir wurden angewiesen, eine mandantenfähige Anwendung zu haben, daher der Versuch der Variablen ThreadLocal. Da es jedoch nicht funktionierte, konnten wir die Anwendung so ausführen, wie wir sie ursprünglich entworfen hatten.

Ich glaube, dass es einen Weg gibt, dies alles zum Laufen zu bringen, es braucht einfach mehr, als in den anderen Beiträgen beschrieben wird. Sie müssen Ihre eigene RepositoryFactoryBean erstellen. Hier ist das Beispiel aus den Spring Data MongoDB Reference Docs . Sie müssen immer noch Ihre eigene MongoTemplate implementieren und den ensureIndexes()-Aufruf verzögern oder entfernen. Sie müssen jedoch einige Klassen umschreiben, um sicherzustellen, dass Ihre MongoTemplate anstelle von Spring's aufgerufen wird. Mit anderen Worten, viel Arbeit. Arbeit, die ich gerne sehen würde oder sogar mache, ich hatte einfach keine Zeit.

Danke für die Antworten.

8
sbzoom

Hier ist ein Link zu einem Artikel, von dem ich denke, dass er danach sucht http://michaelbarnesjr.wordpress.com/2012/01/19/spring-data-mongo/

Der Schlüssel besteht darin, mehrere Vorlagen bereitzustellen

konfigurieren Sie eine Vorlage für jede Datenbank.

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

konfigurieren Sie eine Vorlage für jede Datenbank.

<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>

Nun müssen Sie Spring mitteilen, wo sich Ihre Repositorys befinden, damit sie sie injizieren können. Sie müssen sich alle in demselben Verzeichnis befinden. Ich habe versucht, sie in verschiedenen Unterverzeichnissen zu haben, und es hat nicht richtig funktioniert. Sie befinden sich also alle im Repository-Verzeichnis.

<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>

Jedes Repository ist eine Schnittstelle und wird wie folgt geschrieben (Ja, Sie können sie leer lassen):

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

}

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

}

Der Name der privaten Variablen imageRepository ist die Sammlung! Image.Java wird in der Bildersammlung in der Imagedb-Datenbank gespeichert.

So können Sie finden , einfügen und löschen Datensätze:

@Service
public class ImageService {

    @Autowired
    private ImageRepository imageRepository;
}

Mit Autowiring passen Sie den Variablennamen an den Namen (Id) in Ihrer Konfiguration an.

14
john

Möglicherweise möchten Sie SimpleMongoDbFactory unterteilen und festlegen, wie der von getDb zurückgegebene Standard-DB zurückgegeben wird. Eine Option besteht darin, Thread-lokale Variablen zu verwenden, um sich für die zu verwendende Datenbank zu entscheiden, anstatt mehrere MongoTemplates zu verwenden.

Etwas wie das:

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);
    }
}

Überschreiben Sie dann mongoDBFactory() in Ihrer @Configuration-Klasse, die sich wie folgt aus AbstractMongoConfiguration erstreckt:

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

In Ihrem Client-Code (möglicherweise einem ServletFilter oder einem ähnlichen Filter) müssen Sie vor dem Ausführen von Mongo-Operationen Folgendes aufrufen: ThreadLocalDBNameMongoRepository.setDefaultNameForCurrentThread() Und anschließend mit: ThreadLocalDBNameMongoRepository.clearDefaultNameForCurrentThread() nachdem Sie fertig sind.

8
baja

Die Stelle, die Sie sich ansehen sollten, ist die MongoDbFactory-Schnittstelle. Die grundlegende Implementierung davon erfordert eine Mongo-Instanz und funktioniert damit während der gesamten Anwendungslebensdauer. Um eine Datenbanknutzung pro Thread (und damit pro Anforderung) zu erreichen, müssen Sie wahrscheinlich etwas in der Art von AbstractRoutingDataSource implementieren. Die Idee ist so ziemlich, dass Sie eine Vorlagenmethode haben, die den Mandanten pro Aufruf nachschlagen muss (ThreadLocal bound, denke ich), und dann eine Mongo-Instanz aus einem Satz vordefinierter Instanzen oder einer benutzerdefinierten Logik auswählen, um eine neue Instanz zu erstellen ein neuer Mieter etc.

Beachten Sie, dass MongoDbFactory normalerweise von der getDb()-Methode verwendet wird. Es gibt jedoch Funktionen in MongoDB, die eine getDb(String name) zur Verfügung stellen müssen. DBRefs (etw. wie ein Fremdschlüssel in der relationalen Welt) kann auf Dokumente einer völlig anderen Datenbank verweisen. Wenn Sie also die Delegierung durchführen, vermeiden Sie entweder die Verwendung dieser Funktion (ich denke, die DBRefs, die auf eine andere Datenbank verweist, sind die einzigen Stellen, die getDb(name) aufrufen) oder sie explizit behandeln.

Aus Sicht der Konfiguration könnten Sie mongoDbFactory() entweder ganz überschreiben oder die Basisklasse überhaupt nicht erweitern und Ihre eigene Java-basierte Konfiguration erstellen.

4
Oliver Drotbohm

Ich habe eine andere Datenbank mit Java Config verwendet. So habe ich es gemacht:

@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());    
}

Und der andere war so: 

@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());    
}

Wenn ich also meine Datenbank ändern muss, wähle ich nur die zu verwendende Config aus

1
user3272931