当前位置: 首页 > 知识库问答 >
问题:

如何配置spring-Data-MongoDB以使用共享同一文档模型的两个不同mongo实例

白星渊
2023-03-14

我为一家拥有多个品牌的公司工作,因此我们在一些不同的主机上有几个MongoDB实例,为每个品牌的客户保存相同的文档模型。(结构相同,数据不相同)

为了简单起见,假设我们有一个Orange品牌,其数据库实例服务于端口27017,而Banana品牌的数据库实例服务于端口27018

目前,我正在开发一个欺诈检测服务,它需要连接到所有数据库,并分析所有客户的行为一起,无论品牌。

所以我的“模型”为客户提供了一个共享实体,并用@document.org.springframework.data.mongodb.core.mapping.document注释

接下来我要做的是两个MongoRepositories,比如:

public interface BananaRepository extends MongoRepository<Customer, String>

    List<Customer> findAllByEmail(String email);

public interface OrangeRepository extends MongoRepository<Customer, String>

    List<Customer> findAllByEmail(String email);

使用一些存根方法来通过Id、Email等查找客户。spring负责生成此类接口的所有实现类(相当标准的spring材料)

为了提示每个存储库连接到正确的mongodb实例,我需要两个Mongo配置,例如:


@Configuration
@EnableMongoRepositories(basePackageClasses = {Customer.class})
public class BananaConfig extends AbstractMongoConfiguration {

    @Value("${database.mongodb.banana.username:}")
    private String username;
    @Value("${database.mongodb.banana.database}")
    private String database;
    @Value("${database.mongodb.banana.password:}")
    private String password;
    @Value("${database.mongodb.banana.uri}")
    private String mongoUri;


    @Override
    protected Collection<String> getMappingBasePackages() {
        return Collections.singletonList("com.acme.model");
    }

    @Override
    protected String getDatabaseName() {
        return this.database;
    }

    @Override
    @Bean(name="bananaClient")
    public MongoClient mongoClient() {
        final String authString;

        //todo: Use MongoCredential
        //todo: Use ServerAddress
        //(See https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#repositories) 10.3.4
        if ( valueIsPresent(username) ||valueIsPresent(password)) {
            authString = String.format("%s:%s@", username, password);
        } else {
            authString = "";
        }
        String conecctionString = "mongodb://" + authString + mongoUri + "/" + database;

        System.out.println("Going to connect to: " + conecctionString);

        return new MongoClient(new MongoClientURI(conecctionString, builder()
                .connectTimeout(5000)
                .socketTimeout(8000)
                .readPreference(ReadPreference.secondaryPreferred())
                .writeConcern(ACKNOWLEDGED)));

    }

    @Bean(name = "bananaTemplate")
    public MongoTemplate mongoTemplate(@Qualifier("bananaFactory") MongoDbFactory mongoFactory) {
        return new MongoTemplate(mongoFactory);
    }

    @Bean(name = "bananaFactory")
    public MongoDbFactory mongoFactory() {
        return new SimpleMongoDbFactory(mongoClient(),
                getDatabaseName());
    }



    private static int sizeOfValue(String value){
        if (value == null) return 0;
        return value.length();
    }
    private static boolean valueIsMissing(String value){
        return sizeOfValue(value) == 0;
    }
    private static boolean valueIsPresent(String value){
        return ! valueIsMissing(value);
    }
}

我对Orange也有类似的配置,它指向正确的mongo实例。

那么我的服务是这样的:

    public List<? extends Customer> findAllByEmail(String email) {
        return  Stream.concat(
                bananaRepository.findAllByEmail(email).stream(),
                orangeRepository.findAllByEmail(email).stream())
                .collect(Collectors.toList());
       
    }

请注意,我调用了两个存储库,然后将结果收集回一个列表

我希望发生的是,每个存储库将连接到其相应的mongo实例,并通过其电子邮件查询客户。但这并没有发生。我总是对同一个mongo实例执行查询。但在数据库日志中,我可以看到这两个连接都是由spring建立的。它只使用一个连接来运行两个存储库的查询。

这并不奇怪,因为两个Mongo配置在这里都指向同一个模型包。对啊。但是我也尝试了其他方法,比如创建一个BananaCustomer extends Customer到它自己的Model.Banana包中,另一个OrangeCustomer将Customer扩展到它的Model.Orange包中,同时在每个配置中指定适当的basePackageClasses。但这两个都不起作用,我最终得到的结果是,两个查询都运行在同一个数据库上。

:(

在仔细阅读了几个小时的官方spring数据MongoDB文档,并在这里和那里查看了数千行代码之后,我已经没有选择了:似乎没有人做过我之前想要做的事情。除了这里的这个家伙,他必须做同样的事情,但使用的是JPA而不是MongoDB:链接到文章

好吧,虽然它仍然是spring数据,但它不是MongoDB的。

“我如何明确地告诉每个存储库使用特定的mongo配置?

神奇的自动驾驶规则,除非它不起作用,而且没有人理解它的魔力。

提前道谢。

共有1个答案

弘承运
2023-03-14

嗯,我有一个非常详细的答复,但StackOverflow抱怨说看起来像垃圾邮件,不允许我发帖

完整的答案仍然可以在这里作为Gist文件获得

底线是MongoRepository(接口)和model对象都必须放在同一个包中。

 类似资料:
  • 使用Spring data mongo驱动程序,我想使用单个查询更新mongob中的多个文档,这些文档将具有不同的更新值。我尝试了以下代码,但它对所有符合查询条件的文档具有相同的更新值。 有没有办法用不同的值更新所有文档?

  • 问题内容: 我有2个Mongodb数据库通过2个MongoTemplate-s连接到Spring Boot应用程序: mongoTemplate (默认的bean名称,连接到默认的db) mongoAppTemplate (在运行时连接到另一个数据库) 我有很多使用mongoTemplate的MongoRepository-,但我也想创建一些使用mongoAppTemplate的东西。 如何配置2

  • 我有两个Mongodb数据库连接到一个Spring Boot应用程序,其中有两个MongoTemplate-s: mongoTemplate(默认的bean名称,连接到默认的db) mongoAppTemplate(在运行时连接到另一个数据库) 我有很多使用mongoTemplate的MongoRepository,但我也想创建一些使用mongoAppTemplate的。 如何配置2 MongoR

  • 我有一个人[]有三个人(p1,p2,p3)。Person类有两个属性name和email。 我使用了以下代码。 但我不想这样用。我想使用两个组合框与相同的型号。我尝试使用DefaultComboBoxModel并重写getElementAt()方法,如下所示。 } 问题是如何使用相同的ComboBoxModel在一个JComboBox中添加Person[]的所有名称,并在另一个JComboBox中

  • 例如,以下结果结构将是理想的: -只读接口 -可变实现 或者更好的是,有没有一种不同的方式来实现这一点?

  • 我有Spring启动应用程序,它是接收静态数据,基于一些业务逻辑,我需要将数据转移到两个不同的kafka集群,它们有自己的kerberos密钥提及的jaas文件。 我已经编写了两个不同的生产者实例,在它们的不同对象实例中具有以下属性。 第二制片人 当我将其作为两个服务启动并仅启用生产者实例时,它可以工作,但当我在单个jar中启用两个实例时,只有一个生产者可以工作,其他生产者会遇到身份验证问题。 我