Spring-data-mongodb连接到一个Mongo实例中的多个数据库

sbz*_*oom 24 java spring mongodb spring-data spring-data-mongodb

我使用的是最新的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();
Run Code Online (Sandbox Code Playgroud)

看,很容易.但是spring-data-mongodb不允许使用多个数据库的简单方法.设置连接的首选方法Mongo是扩展AbstractMongoConfiguration类:

您将看到重写以下方法:

getDatabaseName()
Run Code Online (Sandbox Code Playgroud)

因此它强制您使用一个数据库名称.然后构建的存储库接口使用传递给SimpleMongoRepository类的MongoTemplate中的数据库名称.

我到底在哪里粘贴多个数据库名称?我必须创建多个数据库名称,多个MongoTempates(每个数据库名称一个)和多个其他配置类.而且仍然没有让我的存储库接口使用正确的模板.如果有人尝试过这样的事情,请告诉我.如果我搞清楚,我会在这里发布答案.

谢谢.

小智 13

以下是我认为您正在寻找的文章的链接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>
Run Code Online (Sandbox Code Playgroud)

为每个数据库配置模板.

<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>
Run Code Online (Sandbox Code Playgroud)

现在,你需要告诉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>
Run Code Online (Sandbox Code Playgroud)

每个存储库都是一个接口,编写如下(是的,您可以将它们留空):

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

}

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

}
Run Code Online (Sandbox Code Playgroud)

私有变量的名称imageRepository是集合!Image.java将保存到imagedb数据库中的图像集合中.

以下是查找,插入删除记录的方法:

@Service
public class ImageService {

    @Autowired
    private ImageRepository imageRepository;
}
Run Code Online (Sandbox Code Playgroud)

通过自动装配,您可以将变量名称与配置中的名称(id)相匹配.

  • 不幸的是,这不是我想要的.我看到了这样的实现,它确实很好用.只是不适合我的目的.如果您在某些数据库中有某些集合,则此设置.我想要所有数据库中的所有集合.每个客户端只在不同的位置获得相同的模式. (7认同)

baj*_*aja 8

您可能希望对SimpleMongoDbFactory返回的默认数据库进行子类化和策略化getDb.一种选择是使用线程局部变量来决定要使用的Db,而不是使用多个MongoTemplates.

像这样的东西:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,mongoDBFactory()@Configuration类中覆盖,从AbstractMongoConfiguration那样延伸:

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

在您的客户端代码(可能是ServletFilter或其他类似代码)中,您需要调用: ThreadLocalDBNameMongoRepository.setDefaultNameForCurrentThread() 在执行任何Mongo工作之前,然后ThreadLocalDBNameMongoRepository.clearDefaultNameForCurrentThread() 在完成后重置它:


sbz*_*oom 8

因此,经过大量的研究和实验,我得出的结论是,目前的spring-data-mongodb项目尚不可能.我尝试了上面的baja方法并遇到了一个特定的障碍.在MongoTemplate运行其ensureIndexes()从它的构造函数中的方法.此方法调出数据库以确保数据库中存在带注释的索引.启动时MongoTemplate调用的构造函数Spring因此我甚至没有机会设置ThreadLocal变量.我必须在Spring启动时设置默认值,然后在请求进入时更改它.这是不允许的,因为我不想要也没有默认数据库.

一切都没有丢失.我们最初的计划是让每个客户端在自己的应用服务器上运行,指向服务器上自己的MongoDB数据库MongoDB.然后我们可以提供一个-Dprovider=系统变量,每个服务器只运行一个数据库.

我们被指示有一个多租户应用程序,因此尝试ThreadLocal变量.但由于它不起作用,我们能够以我们最初设计的方式运行应用程序.

我相信有一种方法可以使这一切工作,它只需要比其他帖子中描述的更多.你必须自己做RepositoryFactoryBean.以下是Spring Data MongoDB参考文档中的示例.你仍然需要实现自己的,MongoTemplate并延迟或删除ensureIndexes()呼叫.但是你必须重写几个类来确保你的MongoTemplate被调用而不是Spring's.换句话说,做了很多工作.我希望看到的工作发生甚至做,我只是没有时间.

谢谢你的回复.