spring-data-mongodb 在一个 Mongo 实例中连接多个数据库

     2023-02-16     191

关键词:

【中文标题】spring-data-mongodb 在一个 Mongo 实例中连接多个数据库【英文标题】:Spring-data-mongodb connect to multiple databases in one Mongo instance 【发布时间】:2012-08-18 04:53:40 【问题描述】:

我正在使用最新的 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(每个数据库名称一个)和多个其他配置类。这仍然没有让我的存储库接口使用正确的模板。如果有人尝试过这样的事情,请告诉我。如果我弄清楚了,我会在这里发布答案。

谢谢。

【问题讨论】:

@sbzomm 我遇到了同样的情况,你找到解决方案了吗? 试试这个方法 - blog.marcosbarbero.com/…。看起来相当干净且可扩展。 【参考方案1】:

这是我认为您正在寻找的文章的链接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) 匹配。

【讨论】:

不幸的是,这不是我想要的。我看到了这样一个实现,它确实工作得很好。只是不是为了我的目的。此设置是如果您在某些数据库中有某些集合。我想要所有数据库中的所有集合。每个客户端都获得相同的架构,只是在不同的位置。 另请注意,mongo:repository 自 1.1 以来不再存在。 mongo-template-ref 属性现在位于 mongo:repositories 级别。 从 spring data mongodb 1.6.x 开始,mongo:repository 不再是 mongo:repositories 的子节点 @john 我如何使用 java 注释 spring 配置引用 monog-template。 有没有人举例说明这个实现如何使用 Java 配置和注释工作?我似乎无法实现相同的行为。【参考方案2】:

因此,经过大量研究和实验,我得出的结论是,目前的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。换句话说,工作量很大。我想看到或什至做的工作,我只是没有时间。

感谢您的回复。

【讨论】:

有最新版本的解决方案吗,我也面临同样的问题,ensureIndexes 让我死了:( 我查看了MongoTemplate 的源代码,并没有再看到ensureIndexes() - 所以它可能工作。知道的人是@Oliver Gierke,他也发布了这个问题的答案 - 他是主要开发人员之一。 终于解决了这个问题,我使用的是Servlet 3.0初始化,并且在创建工厂时没有在mongocontext中设置应用程序上下文,设置后,现在一切都很顺利 我创建了一个解决相同问题的 github 项目,它能够在每个数据库中创建索引。 github.com/Loki-Afro/multi-tenant-spring-mongodb【参考方案3】:

您可能希望对SimpleMongoDbFactory 进行子类化,并制定如何返回getDb 返回的默认数据库的策略。一种选择是使用线程局部变量来决定要使用的 Db,而不是使用多个 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 等)中,您需要调用: ThreadLocalDBNameMongoRepository.setDefaultNameForCurrentThread() 在进行任何 Mongo 工作并随后将其重置为: ThreadLocalDBNameMongoRepository.clearDefaultNameForCurrentThread() 完成后。

【讨论】:

SimpleMongoRepository 没有 getDb() 方法。所以你不能覆盖它或调用 super.getDb()。该方法隐藏在 MongoTemplate 中。 SimpleMongoRepository 引用了 MongoOptions 而不是 MongoTemplate,因此您也无法访问 getDB()。也许是 ThreadLocalMongoTemplate?我会继续研究。这是一条很好的道路 - 谢谢。 你是对的 - 我在粘贴不正确的类名时犯了一个错误。但本质是一样的,正如 Oliver 在他的评论中所描述的那样。 感谢这个例子。我让它工作起来很容易。有没有办法实现按租户收集的方法。如果您有任何想法,请在this 线程上与我分享。我将不胜感激!【参考方案4】:

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

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

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

【讨论】:

我在是否使用 ThreadLocal 之间纠结。但可能不是。我有时希望 ClientA 从 ClientB 的数据库中读取一些记录。我会进行第二次查询并传递 ClientB 数据库的名称。我真正需要的是一个 MongoRepository 接口(和实现),它为每个查询添加一个“databaseName”。计数()-> 计数(数据库名称)。或者,我可能会使用 MongoTemplate(或 MongoDbFactory)来实例化它们,而不是我的存储库的 @Autowired 实例。这些听起来都不是那么理想。 或者可能是 MongoRepository(和 SimpleMongoRepository)上的 getDB/setDB 方法。然后我可以这样做: myRepository.setDB('name'); myRepository.findOne(id);或者,更好的是 myRepository.setDB('name').findOne(id);我会看看我能解决什么问题。 SimpleMongoRepository 只有 MongoOptions 而没有 MongoTemplate 或 MongoDbFactory。所以似乎没有简单的方法来获取 Repository 中的 DB,它都是抽象的。 另外,我不想要多个 Mongo 实例。我只想要一个,里面有多个数据库。所以我想要多个 MongoTemplates。 我让它工作起来非常容易。有没有办法实现按租户收集的方法。如果您有任何想法,请在this 线程上与我分享。我将不胜感激!【参考方案5】:

我使用 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());    

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

【讨论】:

如何更改要使用的配置? 如果我使用 CrudRepository,你的存储库会怎样?如何将不同的 mongoTemplate 注入不同的仓库【参考方案6】:

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

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

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

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

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

参考资料:

multi-tenant-spring-mongodb

【讨论】:

是否可以在 spring-data-mongodb 中注入自定义 Jackson ObjectMapper?

】是否可以在spring-data-mongodb中注入自定义JacksonObjectMapper?【英文标题】:IsispossibletoinjectacustomJacksonObjectMapperinspring-data-mongodb?【发布时间】:2014-02-2312:50:12【问题描述】:我们在我们的应用程序中使用CodaHale指标,并使用Json模... 查看详情

使用 Kotlin 的 spring-data-mongodb 上的 @Transient 在读取期间导致异常

】使用Kotlin的spring-data-mongodb上的@Transient在读取期间导致异常【英文标题】:@Transientonspring-data-mongodbwithKotlinresultsinexceptionduringread【发布时间】:2019-06-0607:53:51【问题描述】:我的项目中使用Kotlin、SpringBoot2.0和MongoDB(使用SpringDa... 查看详情

使用 spring-data-mongodb 流式传输聚合操作的结果

】使用spring-data-mongodb流式传输聚合操作的结果【英文标题】:Streamingtheresultofanaggregateoperationusingspring-data-mongodb【发布时间】:2017-03-2311:49:28【问题描述】:我正在使用spring-data-mongodb,我想使用游标进行聚合操作。MongoTemplate.stre... 查看详情

mongodb分组函数的使用(spring-data-mongodb)

这两天要做mongodb日志的模块,下面记录一下。一、首先要导入一批数据,使用springboot来完成。配置mongodb的复制集:在application.yml文件中配置uri来完成格式:mongodb://用户名:密码@ip:端口[,ip:端口][,ip:端口]/数据库名下面注入mongoTem... 查看详情

spring-data-mongodb 使用 fieldName 而不是 _id

】spring-data-mongodb使用fieldName而不是_id【英文标题】:spring-data-mongodbusingthefieldNameinsteadof_id【发布时间】:2019-10-0812:46:41【问题描述】:我有一个属性为的PojoClassA@Id@Field("item_id")privateStringitemId;当我尝试根据itemId更新MongoDB集合中... 查看详情

spring-data-mongodb中的mongotemplate与mongorepository

(一)、SpringData概述 SpringData:Spring的一个子项目。用于简化数据库访问,支持NoSQL和关系数据存储。其主要目标是使数据库的访问变得方便快捷。●SpringData项目所支持NoSQL存储:MongoDB(文档数据库)Neo4j(图形数据库)Redis(键... 查看详情

使用 spring-data-mongodb 进行审计

】使用spring-data-mongodb进行审计【英文标题】:Auditingwithspring-data-mongodb【发布时间】:2017-05-2215:59:45【问题描述】:我正在尝试使用springdatamongodb启用自动审计字段,如here所述。下面是我的配置类@Configuration@EnableWebMvc@ComponentScan(b... 查看详情

spring-data-mongodb使用原生aggregate语句(更新中)

除了特殊注释外,本文的测试结果均基于spring-data-mongodb:1.10.6.RELEASE(spring-boot-starter:1.5.6.RELEASE),MongoDB3.0.6   考虑到大多数人都是来找答案的,所以先给出结论//importorg.springframework.data.mongodb.core.MongoTemplate;mongoTempl 查看详情

将 Spring 安全 ACL 与 spring-data-mongodb 一起使用

】将Spring安全ACL与spring-data-mongodb一起使用【英文标题】:UsingSpringsecurityACLswithspring-data-mongodb【发布时间】:2017-11-0704:23:57【问题描述】:spring-security-acl文档声明如下:Pleasenotethatourout-of-the-boxAclServiceandrelateddatabaseclassesalluse 查看详情

如何以 ManyToMany (RDBMS) 的形式加入两个集合 spring-data-mongodb

】如何以ManyToMany(RDBMS)的形式加入两个集合spring-data-mongodb【英文标题】:HowcanIjointwocollectionsspring-data-mongdbasManyToMany(RDBMS)【发布时间】:2017-05-2222:16:59【问题描述】:我有来自https://github.com/szerhusenBC/jwt-spring-security-demo/的这个示... 查看详情

MongoDB Aggregation - 如何使用 spring-data-mongodb 将查询表达式应用到匹配阶段?

】MongoDBAggregation-如何使用spring-data-mongodb将查询表达式应用到匹配阶段?【英文标题】:MongoDBAggregation-Howcaniapplyqueryexpressionintomatchstageusingspring-data-mongodb?【发布时间】:2021-12-0623:59:05【问题描述】:我有包含动态字段的文档,我... 查看详情

关于spring-boot中mongodb包的一个奇怪的版本问题

...项目的不同,发现dependency的版本号不一样。旧的项目中spring-data-mongodb的版本是2.1.2。我开始怀疑maven出问题了,于是把这些包全部删除,刷新maven,还是一样的报错。而且我在mvnrepository.com网站上看到spring-boot-starter-data-mongodb:2.1.0... 查看详情

在同一个 .m 文件中重用 NSString

】在同一个.m文件中重用NSString【英文标题】:ReusingNSStringinthesame.mfile【发布时间】:2009-12-1902:50:42【问题描述】:我有一个字符串(当前定义在我的.h文件中),我想在我的.m文件中填充和重复使用。设置如下:用户在我的界面... 查看详情

如何在 M:M 中强制执行 1:M 关系?

...【发布时间】:2022-01-0419:29:42【问题描述】:我正在设计一个数据库架构。目前有2张桌子:任务(TaskID、TaskName)描述(DescriptionID、描述)一个任务可以有多个描述,但同一个描述不应该被多个任务共享。描述可以有0或1个任务... 查看详情

在同一应用程序中支持 JPA 和 MongoDB

...求:支持几个RDBMS(我打算使用JPA来实现)和MongoDB(首选spring-data-mongodb)来实现持久性。更准确地说,必须配置和使用一个或另一个,我不是在谈论跨商店。程序应如下:编写应用程序,将. 查看详情

如何在 __m128 变量中包含一个整数?

】如何在__m128变量中包含一个整数?【英文标题】:HowcanIhaveina__m128variableawholecomplexnumber?【发布时间】:2016-10-1420:25:14【问题描述】:我没有太多在C中使用内部函数的经验。我的问题是有这个函数voidfunction(complexfloat*A,complexfloat*... 查看详情

mongodb迁移导致key重复

...,因为我们有一个mongo数据库,我们必须从编写此代码的spring-data-mongodb:1.10.18迁移:DBCollectioncontextCollectio 查看详情

在 .m 上声明代表

...4【问题描述】:我对Objective-C比较陌生。我在网上找到了一个代码,它在rootViewController.m上有类似的东西(这是一个基于navigationController的应用程序)。@interfaceRootViewController(CManagerDelegate)<CManage 查看详情