26

我正在使用最新的 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()

所以它迫使你使用一个数据库名称。然后您构建的存储库接口使用传递给类的 MongoTemplate 中的数据库名称SimpleMongoRepository

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

谢谢。

4

6 回答 6

14

这是我认为您正在寻找的文章的链接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 数据库中的图像集合中。

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

@Service
public class ImageService {

    @Autowired
    private ImageRepository imageRepository;
}

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

于 2012-08-31T21:14:35.683 回答
9

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

然后,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());
  }
}

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

于 2012-08-22T22:56:57.403 回答
9

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

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

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

我相信有一种方法可以使这一切正常工作,它只需要比其他帖子中描述的更多。你必须自己做RepositoryFactoryBean。这是Spring Data MongoDB Reference Docs中的示例。您仍然必须实施自己的MongoTemplate并延迟或删除ensureIndexes()呼叫。但是您必须重写一些类以确保MongoTemplate调用您的而不是Spring's. 换句话说,工作量很大。我想看到甚至做的工作,我只是没有时间。

感谢您的回复。

于 2012-10-09T15:17:55.043 回答
4

要看的地方是MongoDbFactory界面。它的基本实现需要一个 Mongo 实例,并在整个应用程序生命周期中使用它。要实现每个线程(以及每个请求)的数据库使用,您可能必须按照AbstractRoutingDataSource的方式实现一些东西。这个想法几乎是你有一个模板方法,它必须在每次调用时查找租户(ThreadLocal我猜是绑定的),然后Mongo从一组预定义的实例或一些自定义逻辑中选择一个实例来为新的租户等

请记住,MongoDbFactory通常 get 是通过该getDb()方法使用的。但是,MongoDB 中有一些特性需要我们提供getDb(String name). DBRefs(就像关系世界中的外键一样)可以指向文档完全不同的数据库。因此,如果您正在执行委托,请避免使用该功能(我认为DBRef指向另一个数据库的 s 是唯一调用的地方getDb(name))或明确处理它。

从配置的角度来看,您可以简单地mongoDbFactory()完全覆盖或根本不扩展基类并提出您自己的基于 Java 的配置。

于 2012-08-23T16:23:48.827 回答
1

我使用 java Config 使用了不同的数据库,这就是我的做法:

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

所以当我需要更改我的数据库时,我只选择要使用的配置

于 2015-09-24T18:06:19.240 回答
-2

据我了解,您需要更灵活地动态更改当前数据库。

我已经链接了一个以简单方式实现多租户的项目。

它可以用作应用程序的起点。

它实现了 SimpleMongoDbFactory 并提供了一个自定义的 getDB 方法来解析正确的数据库以在特定时刻使用。它可以通过多种方式进行改进,例如,通过从 SpringSession 对象的 HttpSession 检索数据库详细信息,例如可以由 Redis 缓存。

要让不同的 mongoTemplates 同时使用不同的数据库,可以将 mongoDbFactory 的范围更改为 session。

参考:

多租户spring-mongodb

于 2017-03-27T11:32:43.187 回答