我正在使用最新的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()
因此它强制您使用一个数据库名称。然后构建的存储库接口使用传递到SimpleMongoRepository
类的MongoTemplate中的数据库名称。
我到底在哪里粘贴多个数据库名称?我必须创建多个数据库名称,多个MongoTempate
(每个数据库名称一个),以及多个其他配置类。而且仍然没有让我的存储库接口使用正确的模板。如果有人尝试过这样的事情,请告诉我。如果我搞清楚,我会在这里发布答案。
感谢。
答案 0 :(得分: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)匹配。
答案 1 :(得分: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);
}
}
然后,覆盖mongoDBFactory()
类中@Configuration
的{{1}},如下所示:
AbstractMongoConfiguration
在您的客户端代码(可能是ServletFilter或其他类似代码)中,您需要调用:
@Bean
@Override
public MongoDbFactory mongoDbFactory() throws Exception {
if (getUserCredentials() == null) {
return new ThreadLocalDbNameMongoDbFactory(mongo(), getDatabaseName());
} else {
return new ThreadLocalDbNameMongoDbFactory(mongo(), getDatabaseName(), getUserCredentials());
}
}
在做任何Mongo工作之前,然后重置它:
ThreadLocalDBNameMongoRepository.setDefaultNameForCurrentThread()
完成后。
答案 2 :(得分:8)
经过大量的研究和实验,我得出的结论是,目前的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
。换句话说,做了很多工作。我希望看到的工作发生甚至做,我只是没有时间。
感谢您的回复。
答案 3 :(得分:4)
要查看的地点是MongoDbFactory
界面。它的基本实现需要一个Mongo实例,并在整个应用程序生命周期中使用它。要实现每线程(以及每个请求)数据库的使用,您可能必须实现AbstractRoutingDataSource行。这个想法几乎就是你有一个模板方法,它必须每次调用查找一个租户(我猜是ThreadLocal
绑定),然后从一组预定义的或一些自定义逻辑中选择一个Mongo
实例为新租户提出一个新的等等。
请注意MongoDbFactory
通常会通过getDb()
方法使用getDb(String name)
。但是,MongoDB中有一些功能需要我们提供DBRef
。 DBRef
s(像关系世界中的外键一样)可以指向文档完全不同的数据库。因此,如果你正在进行委托,要么避免使用该功能(我认为指向另一个数据库的getDb(name)
是唯一调用mongoDbFactory()
的地方)或明确处理它。
从配置的角度来看,你可以简单地完全覆盖{{1}},或者根本不扩展基类,并提出自己的基于Java的配置。
答案 4 :(得分: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());
}
因此,当我需要更改我的数据库时,我只选择使用哪个配置
答案 5 :(得分:-2)
据我了解,您希望在动态更改当前数据库时有更大的灵活性。
我已经以简单的方式链接了一个实现多租户的项目。
它可以作为应用程序的起点。
它实现SimpleMongoDbFactory并提供自定义getDB方法来解析在特定时刻使用的正确数据库。它可以通过多种方式进行改进,例如,通过从SpringSession对象中检索HttpSession中的db详细信息,例如可以由Redis缓存。
要同时使用不同的dbs使用不同的mongoTemplates,可能会将mongoDbFactory的范围更改为session。
参考文献: