web-dev-qa-db-ja.com

Spring-data-mongodbは1つのMongoインスタンスで複数のデータベースに接続します

最新のspring-data-mongodb(1.1.0.M2)と最新のMongo Driver(2.9.0-RC1)を使用しています。私のアプリケーションに接続している複数のクライアントがあり、それぞれに同じMongoサーバー内の独自の「スキーマ/データベース」を与えたい状況があります。ドライバを直接使用している場合、これは達成するのが非常に難しいタスクではありません。

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

ほら、簡単。しかし、spring-data-mongodbでは、複数のデータベースを簡単に使用することはできません。 Mongoへの接続を設定する好ましい方法は、 AbstractMongoConfiguration クラスを拡張することです:

次のメソッドをオーバーライドすることがわかります。

getDatabaseName()

したがって、1つのデータベース名を使用する必要があります。次に構築するリポジトリインターフェイスは、SimpleMongoRepositoryクラスに渡されるMongoTemplate内のデータベース名を使用します。

一体どこに複数のデータベース名を付けますか?複数のデータベース名、複数のMongoTempates(データベース名ごとに1つ)、および他の複数の構成クラスを作成する必要があります。それでも、私のリポジトリインターフェースで正しいテンプレートを使用することはできません。もし誰かがそのようなことを試みたならば、私に知らせてください。分かったら答えをここに掲載します。

ありがとう。

24
sbzoom

したがって、多くの研究と実験の結果、これは現在の_spring-data-mongodb_プロジェクトではまだ可能ではないと結論付けました。上記のバハの方法を試しましたが、特定のハードルにぶつかりました。 MongoTemplateは、コンストラクター内からensureIndexes()メソッドを実行します。このメソッドは、データベースを呼び出して、注釈付きインデックスがデータベースに存在することを確認します。 MongoTemplateの起動時にSpringのコンストラクターが呼び出されるため、ThreadLocal変数を設定する機会すらありません。私はSpringの開始時にデフォルトを設定しておく必要があり、リクエストが来たときにそれを変更する必要があります。これは、デフォルトのデータベースが必要ないため、許可されていません。

しかし、すべてが失われたわけではありません。私たちの当初の計画は、各クライアントを独自のアプリケーションサーバーで実行し、MongoDBサーバー上の独自のMongoDBデータベースを指すようにすることでした。次に、_-Dprovider=_システム変数を指定すると、各サーバーは1つのデータベースのみをポイントして実行されます。

マルチテナントアプリケーションを使用するように指示されたため、ThreadLocal変数で試行しました。しかし、機能しなかったため、アプリケーションを最初に設計した方法で実行できました。

これをすべて機能させる方法はあると思いますが、他の投稿で説明されている以上の時間がかかります。自分でRepositoryFactoryBeanを作成する必要があります。 Spring Data MongoDB Reference Docs の例を次に示します。独自のMongoTemplateを実装し、ensureIndexes()の呼び出しを遅延または削除する必要があります。ただし、_Spring's_ではなくMongoTemplateが呼び出されるようにするには、いくつかのクラスを書き直す必要があります。つまり、たくさんの仕事。私が見たい、あるいはしたい仕事、私には時間がありませんでした。

回答ありがとうございます。

8
sbzoom

これは私があなたが探しているものだと思う記事へのリンクです http://michaelbarnesjr.wordpress.com/2012/01/19/spring-data-mongo/

キーは、複数のテンプレートを提供することです

各データベースのテンプレートを構成します。

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

各データベースのテンプレートを構成します。

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

ここで、リポジトリを挿入できるように、リポジトリの場所をSpringに通知する必要があります。それらはすべて同じディレクトリにある必要があります。それらを別のサブディレクトリに入れようとしたところ、正しく機能しませんでした。したがって、これらはすべてリポジトリー・ディレクトリーにあります。

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

各リポジトリはインターフェイスであり、次のように記述されます(はい、空白のままにできます)。

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

}

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

}

プライベート変数imageRepositoryの名前はコレクションです! Image.Javaは、imagedbデータベース内の画像コレクションに保存されます。

findinsertdeleteレコード:

@Service
public class ImageService {

    @Autowired
    private ImageRepository imageRepository;
}

自動配線により、変数名を構成内の名前(id)と照合します。

13
john

SimpleMongoDbFactoryをサブクラス化し、getDbによって返されるデフォルトのDBがどのように返されるかを戦略化することができます。 1つのオプションは、複数のMongoTemplateを使用する代わりに、スレッドローカル変数を使用して、使用するデータベースを決定することです。

このようなもの:

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

次に、AbstractMongoConfigurationから拡張する_@Configuration_クラスのmongoDBFactory()を次のようにオーバーライドします。

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

クライアントコード(おそらくServletFilterなど)で、Mongoの作業を行う前にThreadLocalDBNameMongoRepository.setDefaultNameForCurrentThread()を呼び出し、その後にThreadLocalDBNameMongoRepository.clearDefaultNameForCurrentThread()を呼び出してリセットする必要があります。

8
baja

注目すべき場所はMongoDbFactoryインターフェースです。その基本的な実装は、Mongoインスタンスを取得し、アプリケーションの全ライフタイムを通じてそれを使用します。スレッドごと(したがってリクエストごと)のデータベースの使用を実現するには、おそらく AbstractRoutingDataSource の行に沿って何かを実装する必要があります。呼び出しごとにテナントを検索する必要があるテンプレートメソッドがあり(ThreadLocalバインドされていると思います)、次に、事前定義されたインスタンスのセットまたはいくつかからMongoインスタンスを選択するという考えがほとんどです新しいテナントなどのために新しいものを思いつくためのカスタムロジック.

MongoDbFactoryは通常、getDb()メソッドを通じて使用されることに注意してください。ただし、MongoDBにはgetDb(String name)を提供する必要がある機能があります。 DBRefs(リレーショナル世界の外部キーのようなもの)は、まったく異なるデータベースのドキュメントを指すことができます。したがって、委任を行う場合は、その機能の使用を回避するか(別のDBを指すDBRefsがgetDb(name)を呼び出す唯一の場所だと思う)、または明示的に処理します。

構成の観点からは、mongoDbFactory()を完全にオーバーライドするか、基本クラスをまったく拡張せずに、独自のJavaベースの構成を作成することができます。

4
Oliver Drotbohm

Java Configを使用して別のDBを使用しました、これは私がそれをどのようにしたかです:

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

そしてもう一つはこのようなものでした:

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

データベースを変更する必要があるときは、使用する構成のみを選択します

1
user3272931